让我们从一个示例代码开始:
import scala.reflect.runtime.universe._
class A[T] {
}
def foo[T: TypeTag](a: A[T]) {
println(typeTag[T])
}
val a = new A[Int]
val b: A[_] = a
foo(a)
foo(b)
输出为:
TypeTag[Int]
TypeTag[_$1]
嗯,我不知道TypeTag[_$1]
是什么,但它看起来确实很漂亮。尽管如此,我还是认为foo对世界的期望在这里被违反了,因为如果foo被A[Int]
的实例调用,那么它就保证具有Int类型标签,而不是其他一些模糊的类型标签。此外,与weektypetags相比,typetags不应该是"具体的"吗?
现在我当然看到编译器不能在第二个foo调用中告诉a的类型参数,所以我的期望不是我神奇地得到typeTag[Int]
作为第二个调用的输出,而是我希望有一个编译时错误。
我的误解在哪里?
一些无用的猜测有人可能会说,正在发生的事情是用参数类型A[_]
调用foo,或者更明确地说,是存在类型A[S] forSome { type S }
。然后typeTag以某种方式捕获"_"。但这实际上没有意义,因为foo对某些具体的T
期望A[T]
,而上述存在类型对任何具体的T
都不是A[T]
。或者是带参数A[Any]
的调用?但为什么不是TypeTag[Any]
,更严重的是,A
不是协变的,所以这也是明显错误的。
Type标记是在编译时创建的,值在编译时不可用。一旦将A[Int]
强制转换为存在类型A[_]
,有关该类型参数的所有信息都将丢失。
如果它们是在运行时根据值创建的,则类型擦除将使不可能知道A
的参数。即使在编译时已知是A[Int]
的东西,在运行时也最多是A[Object]
(除非A
是Array
,但我们不去那里)。
所以你的猜测是正确的,对于foo(b)
,类型参数确实是来自存在的A[_]
的_
,并且匿名类型变量打印为_$1
。