如果与ClassTag

时间:2015-06-16 20:24:46

标签: scala reflection covariance

方差与ClassTagsTypeTags之间的联系是什么?

我有两种类型T1T2,用作类型参数。

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中所示。

1 个答案:

答案 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似乎有效。