方差与ClassTags
或TypeTags
之间的联系是什么?
我有两种类型T1
和T2
,用作类型参数。
case class T1()
case class T2()
我有一个带有不变类型参数和一个子类的抽象类,如果我想检查类型参数的类型,它只有在模式中没有类型检查时才有效,就在后卫中。如果存在类型测试,它总是选择第一种情况。
类型检查是必要的,因为在我的实际代码中,我想为每种类型调用不同的函数。 In[T1]
和In[T2]
有单独的功能。
abstract class In[T]
case class VIn[T]() extends In[T]
def test1[T:ClassTag](v:In[T]) = v match {
case x : VIn[T1@unchecked] if classTag[T] == classTag[T1] => "T1"
case y : VIn[T2@unchecked] if classTag[T] == classTag[T2] => "T2"
}
test1(VIn[T1]()) //T1
test1(VIn[T2]()) //T1 !!!
def test2[T:ClassTag](v:In[T]) = v match {
case x if classTag[T] == classTag[T1] => "T1"
case y if classTag[T] == classTag[T2] => "T2"
}
test2(VIn[T1]()) //T1
test2(VIn[T2]()) //T2
在使用许多示例中使用的List
类型时,我意识到如果将类型参数更改为协变,则它在两个测试中都有效。
abstract class Co[+T]
case class VCo[T]() extends Co[T]
def test1[T:ClassTag](v:Co[T]) = v match {
case x : VCo[T1@unchecked] if classTag[T] == classTag[T1] => "T1"
case y : VCo[T2@unchecked] if classTag[T] == classTag[T2] => "T2"
}
test1(VCo[T1]()) // T1
test1(VCo[T2]()) // T2
def test2[T:ClassTag](v:Co[T]) = v match {
case x if classTag[T] == classTag[T1] => "T1"
case y if classTag[T] == classTag[T2] => "T2"
}
test2(VCo[T1]()) // T1
test2(VCo[T2]()) // T2
为什么invarinat类型的第一次测试失败?没有编译器警告或运行时错误,它只选择第一种情况,但警卫显然是错误的,如test2
中所示。
答案 0 :(得分:4)
我绝对认为这是一个编译器错误。
def test1[T:ClassTag](v:In[T]) = {
val t1 = classTag[T] == classTag[T1]
val t2 = classTag[T] == classTag[T2]
println(v match {
case x : VIn[T1@unchecked] if t1 => "T1"
case y : VIn[T2@unchecked] if t2 => "T2"
})
v match {
case x:In[T1] if classTag[T] == classTag[T1] => "T1"
case y: In[T2] if classTag[T] == classTag[T2] => "T2"
}}
使用-Xprint:typer
打印显示(evidence
):
def test1[T](v: In[T])(implicit evidence$1: scala.reflect.ClassTag[T]): String = {
....
val t1: Boolean = scala.reflect.`package`.classTag[T](evidence$1).==
(scala.reflect.`package`.classTag[T1]((ClassTag.apply[T1](classOf[T1]): scala.reflect.ClassTag[T1])))
val t2: Boolean = scala.reflect.`package`.classTag[T](evidence$1).==
(scala.reflect.`package`.classTag[T2]((ClassTag.apply[T2](classOf[T2]): scala.reflect.ClassTag[T2]))) //classTag[T] == classTag[T2]
模式匹配的if
语句是:
scala.reflect.`package`.classTag[T](evidence$1).==(scala.reflect.`package`.classTag[T1](evidence$1))
scala.reflect.`package`.classTag[T](evidence$1).==(scala.reflect.`package`.classTag[T2](evidence$1))
编译器正在将evidence$1
传递给隐式参数classTag[1]()
和classTag[2]()
。所以它实际上是在与自身进行比较。作为一种解决方法,正如您所建议的test2
或预先计算的if
似乎有效。