如果与 ClassTag 一起使用,模式匹配会选择错误的大小写

Pattern Matching chooses the wrong case if used with ClassTag

方差和ClassTagsTypeTags有什么关系?

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

case class T1()
case class T2()

我有一个带有不变类型参数和一个子class的抽象class,如果我想检查类型参数的类型,它只有在没有类型检查的情况下才有效格局,就在守卫之中。如果存在类型测试,它总是选择第一种情况。

类型检查是必要的,因为在我的真实代码中我想为每种类型调用不同的函数。 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.

所示

我绝对认为这是一个编译器错误。

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: scala.reflect.ClassTag[T]): String = {
....

val t1: Boolean = scala.reflect.`package`.classTag[T](evidence).==
         (scala.reflect.`package`.classTag[T1]((ClassTag.apply[T1](classOf[T1]): scala.reflect.ClassTag[T1]))) 


val t2: Boolean = scala.reflect.`package`.classTag[T](evidence).==        
        (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).==(scala.reflect.`package`.classTag[T1](evidence))

scala.reflect.`package`.classTag[T](evidence).==(scala.reflect.`package`.classTag[T2](evidence))

编译器正在将 evidence 传递给 classTag[1]()classTag[2]() 的隐式参数。所以它实际上是在与自己进行比较。作为解决方法,正如您所建议的 test2 或预先计算的 if 似乎可行。