Scala 的 ClassTag 和 TypeTag一直都是用时查阅一下资料,用完又忘了,一直没有个记录和总结。今天做一个简单的总结,并且不断更新完善。
首先看官网给出的解释
scala.reflect.api.TypeTags#TypeTag. A full type descriptor of a Scala type. For example, aTypeTag[List[String]]contains all type information, in this case, of typescala.List[String].scala.reflect.ClassTag. A partial type descriptor of a Scala type. For example, aClassTag[List[String]]contains only the erased class type information, in this case, of typescala.collection.immutable.List.ClassTags provide access only to the runtime class of a type. Analogous toscala.reflect.ClassManifest.
也是就是说ClassTag保留的是泛型擦除后的信息,而TypeTag保留的是非泛型擦除后的信息,保留的信息更完整。
获取ClassTag & TypeTag(已知类型)
import scala.reflect._ val ct = classTag[String]
import scala.reflect.runtime.universe._ val tt = typeTag[Int]
查看下 classTag 和 typeTag 的源码:
def classTag[T](implicit ctag: ClassTag[T]) = ctag
def typeTag[T](implicit ttag: TypeTag[T]) = ttag
这些implicit参数是由编译器生成的,因此上述获取 ClassTag 和 TypeTag 的方法也可以采用如下的写法:
val ct = implicitly[ClassTag[Int]] val tt = implicitly[TypeTag[Int]]
implicitly[T]的作用就是步骤类型为T的implicit参数并返回。implicitly的源码如下:
def implicitly[T](implicit e: T) = e
获取运行时的ClassTag & TypeTag
对于运行时的对象获取ClassTag和TypeTag可以采用下列方法:
val list = List[Int](1, 2, 3) def getClassTag[T: ClassTag](value: T) = { //implicitly[ClassTag[T]] classTag[T] } println(getClassTag(list)) // scala.collection.immutable.List def getTypeTag[T: TypeTag](value: T) = { //implicitly[TypeTag[T]] typeTag[T] } println(getTypeTag(list)) // TypeTag[List[Int]]
通过上下文绑定,编译器会帮我们生成对应的隐式参数。
获取Class和Type对象
由ClassTag我们可以获得对应的Class对象,在Java中被称为类的类类型,可以ct.runtimeClass获得,获得的Class是泛型擦除后的类型,等价于classOf[T]。
由TypeTag我们可以过得对应的Type对象,Type对象相对于Class对象,保存的信息更为完整,是Scala鉴于Java的Class对象只能获得泛型擦除后类的信息的一个补充,通过tt.tpe获得,等价于typeOf[T]
ClassTag和TypeTag的作用
在Java中创建Collection类型的对象都必须提供明确的类型,而Scala中可以通过ClassTag实现创建“泛型数组”。由于ClassTag的保留有对象的类型信息,所以可以通过下列方法创建在编译期无法知道类型的数组:
scala> def mkArray[T : ClassTag](elems: T*) = Array[T](elems: _*) mkArray: [T](elems: T*)(implicit evidence$1: scala.reflect.ClassTag[T])Array[T] scala> mkArray(42, 13) res0: Array[Int] = Array(42, 13) scala> mkArray(List(1), List(2), List(3)) res1: Array[List[Int]] = Array(List(1), List(2), List(3)) scala> mkArray(List(1), List(2), List("1", "3")) res2: Array[List[Any]] = Array(List(1), List(2), List(1, 3))
从上面也可以看到ClassTag保留的是泛型擦除后的信息。
而TypeTag由于保留更多的类型信息,因此更多的用于反射中。
参考:http://docs.scala-lang.org/overviews/reflection/typetags-manifests.html