带有TypeTag的泛型类型上的Scala模式匹配会生成警告,而ClassTag则不会



我有两个非常相似的方法。唯一的区别是使用ClassTagTypeTag:

def matchClass[A: ClassTag](v: Any) =
    v match {
        case a: A => "it's A"
        case _ => "not A"
    }
def matchType[A: TypeTag](v: Any) = ... // same code as matchClass

matchType将显示编译警告,但matchClass不会显示:
abstract type pattern A is unchecked since it is eliminated by erasure case a: A

为什么会有警告?为什么只显示TypeTag而不显示ClassTag

您不会看到classTag的警告,因为该检查只适用于以下情况:

scala> matchClass[Int]("aaa")
res82: String = not A
scala> matchClass[Int](5)
res83: String = it's A 

并且不适用于typeTag:

scala> matchType[Int](5)
res84: String = it's A
scala> matchType[Int]("aaa")
res85: String = it's A

原因是,对于classTags上的模式匹配(当它看到隐式时),编译器会生成如下内容:

case a: A if classTag[A].runtimeClass.isInstance(a) => ...

一般来说,无法为TypeTag获取runtimeClass(考虑到编译和运行时,请参阅UPDATE,了解仅允许在运行时提取的特定情况),所以这就是编译器不转换它们的原因。默认情况下,由于擦除,模式匹配无法在通用(多态)类型上匹配,因此您可以在默认情况下看到警告:

 scala> def matchGeneric[A](v: Any) =
 |     v match {
 |         case a: A => "it's A"
 |         case _ => "not A"
 |     }
<console>:28: warning: abstract type pattern A is unchecked since it is eliminated by erasure
               case a: A => "it's A"
                       ^
matchGeneric: [A](v: Any)String

更新:正如@Seth Tisue所提到的,当一个标签来自运行时宇宙(仅)时,你可以为它获得一个运行时类(但你必须首先创建一个镜像)。


参考:

根据ClassTag本身的scaladocs:

编译器试图通过将(_: T)类型模式包装为ct(_: T)(其中ctClassTag[T]实例),将模式匹配中未检查的类型测试转换为已检查的类型。调用其他提取器之前所需的类型测试也会进行类似处理。如果SomeExtractor.unapply(x: T)中的T是不可检查的,则SomeExtractor(...)变为ct(SomeExtractor(...)),但我们有一个ClassTag[T]的实例。

TypeTag scaladocs和语言规范本身没有提到TypeTags 的任何此类功能


推测性解释

无法确定为什么会实现某些功能,因此任何猜测都是固执己见的(超出了so的范围,甚至与您的问题无关,而是为了回答@Tom的评论)。尽管如此(截至2.12)…

这可能是因为"ClassTags只提供对某个类型的运行时类的访问",并且是标量库的一部分(即使它们的包是scala.reflect.),而TypeTags是单独的(并且相当宽的)反射API的一部分,因此意味着根据它们所在的universe引用编译或运行时,因此,为这些代码编写额外的检查(在合成过程中!!)不仅会让(用户)感到困惑,而且对语言开发人员来说也很困难:合成本身在一个不同的(编译器)模块中,[合成]不依赖于反射(只有scala库),因为模式匹配合成是在早期的"patmat"阶段进行的。此外,scala规范(12.3.4.2方差)提到使用ClassTag对数组实例化进行综合调整,这(非常推测)可能意味着ClassTag与"语法糖"功能更加集成。

最新更新