class Foo
class Bar extends Foo
def meth[A](xs: List[A]) = xs match {
case _: List[String] => "list of strings"
case _: List[Foo] => "list of foos"
}
我们会收到警告:
<console>:23: warning: non-variable type argument String in type pattern List[String]↩
is unchecked since it is eliminated by erasure
case _: List[String] => "list of strings"
^
<console>:24: warning: non-variable type argument Foo in type pattern List[Foo]↩
is unchecked since it is eliminated by erasure
case _: List[Foo] => "list of foos"
^
scala> class Foo{class Bar}
defined class Foo
scala> def m(f: Foo)(b: f.Bar)(implicit ev: Manifest[f.Bar]) = ev
warning: there were 2 deprecation warnings; re-run with -deprecation for details
m: (f: Foo)(b: f.Bar)(implicit ev: Manifest[f.Bar])Manifest[f.Bar]
scala> val f1 = new Foo;val b1 = new f1.Bar
f1: Foo = Foo@681e731c
b1: f1.Bar = Foo$Bar@271768ab
scala> val f2 = new Foo;val b2 = new f2.Bar
f2: Foo = Foo@3e50039c
b2: f2.Bar = Foo$Bar@771d16b9
scala> val ev1 = m(f1)(b1)
warning: there were 2 deprecation warnings; re-run with -deprecation for details
ev1: Manifest[f1.Bar] = Foo@681e731c.type#Foo$Bar
scala> val ev2 = m(f2)(b2)
warning: there were 2 deprecation warnings; re-run with -deprecation for details
ev2: Manifest[f2.Bar] = Foo@3e50039c.type#Foo$Bar
scala> ev1 == ev2 // they should be different, thus the result is wrong
res28: Boolean = true
scala> def m(f: Foo)(b: f.Bar)(implicit ev: TypeTag[f.Bar]) = ev
m: (f: Foo)(b: f.Bar)(implicit ev: reflect.runtime.universe.TypeTag[f.Bar])↩
reflect.runtime.universe.TypeTag[f.Bar]
scala> val ev1 = m(f1)(b1)
ev1: reflect.runtime.universe.TypeTag[f1.Bar] = TypeTag[f1.Bar]
scala> val ev2 = m(f2)(b2)
ev2: reflect.runtime.universe.TypeTag[f2.Bar] = TypeTag[f2.Bar]
scala> ev1 == ev2 // the result is correct, the type tags are different
res30: Boolean = false
scala> ev1.tpe =:= ev2.tpe // this result is correct, too
res31: Boolean = false
它们也很容易用来检查类型参数:
import scala.reflect.runtime.universe._
def meth[A : TypeTag](xs: List[A]) = typeOf[A] match {
case t if t =:= typeOf[String] => "list of strings"
case t if t <:< typeOf[Foo] => "list of foos"
}
scala> meth(List("string"))
res67: String = list of strings
scala> meth(List(new Bar))
res68: String = list of foos
object Macro {
import language.experimental.macros
import scala.reflect.macros.Context
def anymacro[A](expr: A): String = macro __anymacro[A]
def __anymacro[A : c.WeakTypeTag](c: Context)(expr: c.Expr[A]): c.Expr[A] = {
// to get a Type for A the c.WeakTypeTag context bound must be added
val aType = implicitly[c.WeakTypeTag[A]].tpe
???
}
}
如果用 TypeTag 替换 WeakTypeTag ,则抛出错误:
<console>:17: error: macro implementation has wrong shape:
required: (c: scala.reflect.macros.Context)(expr: c.Expr[A]): c.Expr[String]
found : (c: scala.reflect.macros.Context)(expr: c.Expr[A])(implicit evidence$1: c.TypeTag[A]): c.Expr[A]
macro implementations cannot have implicit parameters other than WeakTypeTag evidences
def anymacro[A](expr: A): String = macro __anymacro[A]
^
1 回答
TypeTag
解决了Scala的类型在运行时被擦除的问题(类型擦除) . 如果我们想做我们会收到警告:
为了解决这个问题,我向Scala介绍了Manifests . 但它们的问题是无法表示很多有用的类型,比如路径依赖类型:
因此,它们被_1566385替换,它们更易于使用并且很好地集成到新的Reflection API中 . 有了它们,我们可以优雅地解决上面关于路径依赖类型的问题:
它们也很容易用来检查类型参数:
此时,理解使用
=:=
(类型相等)和<:<
(子类型关系)进行相等性检查是非常重要的 . 永远不要使用==
或!=
,除非你完全知道你做了什么:后者检查结构相等性,这通常不是应该做的,因为它不关心诸如前缀之类的事情(如示例中所示) .
TypeTag
是完全由编译器生成的,这意味着编译器在调用期望这样的TypeTag
的方法时创建并填充TypeTag
. 存在三种不同形式的标签:scala.reflect.ClassTag
scala.reflect.api.TypeTags#TypeTag
scala.reflect.api.TypeTags#WeakTypeTag
ClassTag
替代ClassManifest
而TypeTag
或多或少是Manifest
的替代品 .前者允许完全使用通用数组:
ClassTag
仅提供在运行时创建类型所需的信息(类型已擦除):如上所述,他们不关心类型擦除,因此如果需要"full"类型
TypeTag
应该使用:可以看出,
TypeTag
的方法tpe
会产生一个完整的Type
,这与调用typeOf
时的结果相同 . 当然,可以同时使用ClassTag
和TypeTag
:现在剩下的问题是
WeakTypeTag
的意义是什么?简而言之,TypeTag
表示具体类型(这意味着它只允许完全实例化的类型),而WeakTypeTag
只允许任何类型 . 大多数情况下,人们并不关心哪些是什么(这意味着应该使用TypeTag
),但是例如,当使用宏时,它们应该使用泛型类型:如果用
TypeTag
替换WeakTypeTag
,则抛出错误:有关
TypeTag
和WeakTypeTag
之间差异的更详细说明,请参阅此问题:Scala Macros: “cannot create TypeTag from a type T having unresolved type parameters”Scala的官方文档站点还包含guide for Reflection .