让我们从示例代码开始:
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对这个世界的期望在这里被违反了,因为如果用A[Int]
的实例调用foo,那么它保证有Int的typetag,而不是其他一些模糊的typetag。另外,与weektypetags相比,typetags不应该是“具体的”吗?
现在我当然看到编译器无法告诉第二个foo调用A的类型参数。所以我的期望不是我神奇地得到typeTag[Int]
作为第二个调用的输出,而是我希望编译时错误。
我的误解在哪里?
一些徒劳的猜测
有人可能会争辩说,正在进行的是使用参数类型A[_]
调用foo,或者为了更明确地调用存在类型A[S] forSome { type S }
。然后typeTag以某种方式捕获“_”。但这并没有多大意义,因为对于某些具体的A[T]
,foo期望T
,而对于任何具体的A[T]
,上述存在类型不是T
。
或者可能是一个带有参数A[Any]
的电话?但是,为什么不TypeTag[Any]
,更严重的是,A
不是协变的,所以这也是完全错误的。
答案 0 :(得分:1)
在编译时创建类型标记,并且在编译时值不可用。将A[Int]
转换为存在类型A[_]
后,有关类型参数的所有信息都将丢失。
如果它们是在运行时创建的,则根据值,类型擦除将无法知道A
的参数。即使在编译时已知为A[Int]
的内容在运行时最多只有A[Object]
(除非A
为Array
,但我们不会去那里)
所以你的推测是正确的,对于foo(b)
,类型参数确实来自存在主义_
的{{1}},并且匿名类型变量打印为A[_]
。< / p>