我有两个非常相似的方法。唯一的区别是使用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
?
答案 0 :(得分:8)
您没有看到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) => ...
一般来说,runtimeClass
无法获得TypeTag
(考虑编译和运行时,请参阅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提到when a tag comes from run-time universe (only)你可以为它获得一个运行时类(但你必须先创建一个镜像)。
参考:
根据ClassTag
本身的scaladocs:
编译器尝试通过将
(_: T)
类型模式包装为ct(_: T)
来将模式匹配中未经检查的类型测试转换为已检查的模式,其中ct
是ClassTag[T]
实例。在调用其他提取器之前必须进行类型测试。如果SomeExtractor(...)
中的ct(SomeExtractor(...))
无法检查,则T
会变为SomeExtractor.unapply(x: T)
,但我们的实例为ClassTag[T]
。
TypeTag
scaladocs和语言规范本身并未提及TypeTags
推测性解释
没有办法确定某些功能是否实现的原因,因此任何推测都是固执的(并且超出SO范围,甚至不直接与您的问题相关,而是回答@汤姆的评论)。尽管如此(截至2.12)......
这可能是因为“ClassTags仅提供对类型的运行时类的访问”并且是scala-library的一部分(即使它们的包是scala.reflect.
),而TypeTags是单独的一部分(并且相当广泛)反射API因此意味着根据它们所在的universe
来指代编译或运行时间,因此为那些编写额外的检查(在合成期间!!)不仅仅是混淆(对于用户)但对语言开发人员来说也很难:合成本身在不同的(compiler)模块中,[综合]不依赖于reflection(只有scala-library)作为模式匹配合成正在早期的“patmat”舞台上发生。此外,scala spec(12.3.4.2 Variance)提到了ClassTag的使用,用于数组实例的合成调整(非常推测性地)可能意味着ClassTag与“syntax sugar”特征更加集成。