我有两个非常相似的方法。唯一的区别是使用ClassTag
和TypeTag
:
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)
(其中ct
是ClassTag[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与"语法糖"功能更加集成。