注:本文为译文,原文是Scala官方文档中的1篇,这里翻译出来作为本系列的1篇文章。原文链接: http://docs.scala-lang.org/overviews/reflection/typetags-manifests.html。 本文原文出处: http://blog.csdn.net/bluishglc/article/details/52596696 严禁任何情势的转载,否则将拜托CSDN官方保护权益!
犹如所有其他的JVM语言,Scala(在范型中)的类型信息在编译期都被擦除,这意味着如果你在运行时去检测1些实例的类型,你可能没法获得全部的类型信息,即便Scala的编译器在编译时期都知道这些信息。
像scala.reflect.Manifest1样, TypeTags可以被认为是携带了编译期全部类型信息到运行期的对象,例如,TypeTag[T]封装了在编译时期肯定的T的实际类型信息,而这些信息可以在运行时期使用(为表述更清晰,本句翻译在原文基础上做了调剂),你要记住,TypeTags可以视作Scala 2.10之前的Manifest类的1个更加丰富的替换者,它完全和Scala的反射系统继承在了1起。
Scala有3种不同类型的TypeTags
犹如Manifest,TypeTag总是由编译器生成的,可以通过3种方式获得。
你可以通过typeTag方法直接获得某个特定类型的TypeTag,typeTag方法在Universe。
例如, 获得Int型的1个TypeTag:
import scala.reflect.runtime.universe._
val tt = typeTag[Int]
或获得String型的1个ClassTag:
import scala.reflect._
val ct = classTag[String]
这些方法基于给定的类型参数T构建了TypeTag[T] 或ClassTag[T]的实例(译者注:在编译期由编译器自动完成)。
像Manifest1样,你实际上可以要求编译器去生成1个TypeTag, 这可以通过指定1个TypeTag[T]的隐式的“迹象”(evidence)参数来实现。如果编译器在编译期间没能找到匹配的隐式值,编译器就会自动创建1个TypeTag[T]的实例(译者注:固然,这时候类型T已肯定)。
注意:在方法和类上使用TypeTag[T],ClassTag[T]或WeakTypeTag[T]的1个隐式参数的做法是很常见的。
例如,我们可以写1个接受任何类型做参数的方法,然后通过TypeTag打印这个参数的类型信息:
import scala.reflect.runtime.universe._
def paramInfo[T](x: T)(implicit tag: TypeTag[T]): Unit = {
val targs = tag.tpe match { case TypeRef(_, _, args) => args }
println(s"type of $x has type arguments $targs")
}
在这里,我们写了1个范型方法paramInfo,同时提供了1个隐式参数(implicit tag: TypeTag[T])。然后我们就能够直接获得类型信息(TypeTag的tpe方法返回的是T的Type实例)。
(译者注:上述方法的意图是:如果T是援用类型(TypeRef),获得它的类型参数列表。)
我们可以这样使用我们的paramInfo方法。
scala> paramInfo(42)
type of 42 has type arguments List()
scala> paramInfo(List(1, 2))
type of List(1, 2) has type arguments List(Int)
(译者注:targs 本身是1个List,42是简单类型,没有类型参数,所以是1个空的List,List(1, 2)明显是1个List[Int],所以它的类型参数是Int)
1种更加简单地实现上述目标的做法是使用1个类型参数的Context Bound。不同于提供1个独立的隐式参数,你可以像下面这样把TypeTag简单的加到类型参数列表中:
def myMethod[T: TypeTag] = ...
基于给定的上下文边界[T: TypeTag],编译器会简单地生成1个TypeTag[T]的隐式参数,然后把方法重写成像前1个带隐式参数的例子那样。
上面使用上下文边界的例子会被(编译器)重写为:
import scala.reflect.runtime.universe._
def paramInfo[T: TypeTag](x: T): Unit = {
val targs = typeOf[T] match { case TypeRef(_, _, args) => args }
println(s"type of $x has type arguments $targs")
}
scala> paramInfo(42)
type of 42 has type arguments List()
scala> paramInfo(List(1, 2))
type of List(1, 2) has type arguments List(Int)
WeakTypeTag[T]泛化了TypeTag[T]。不像1个常规的TypeTag,它的类型描写组件可以指向类型参数或抽象类型,但是,WeakTypeTag[T]总是尽量的具体化。比如,如果被援用类型参数或抽象类型的类型标签是可用的,它们通常会被嵌入到WeakTypeTag[T]的具体类型中去。让我们继续上面的例子:
import scala.reflect.runtime.universe._
def weakParamInfo[T](x: T)(implicit tag: WeakTypeTag[T]): Unit = {
val targs = tag.tpe match { case TypeRef(_, _, args) => args }
println(s"type of $x has type arguments $targs")
}
scala> def foo[T] = weakParamInfo(List[T]())
foo: [T]=> Unit
scala> foo[Int]
type of List() has type arguments List(T)
宽泛地说,TypeTags相当于2.10版本之前的scala.reflect.Manifests是相1致的。而scala.reflect.ClassTag相当于cala.reflect.ClassManifest,scala.reflect.api.TypeTags#TypeTag最贴近scala.reflect.Manifest。其他2.10版本之前的Manifest类型就没有对应的”Tag”类型了。
在Scala 2.10里,scala.reflect.ClassManifests不再被推荐使用!scala.reflect.Manifest也将被TypeTags和ClassTag所代表的风格所取代。