标签归档:scala

Scala ClassTag & TypeTag

Scala 的 ClassTag 和 TypeTag一直都是用时查阅一下资料,用完又忘了,一直没有个记录和总结。今天做一个简单的总结,并且不断更新完善。

首先看官网给出的解释

  1. scala.reflect.api.TypeTags#TypeTag. A full type descriptor of a Scala type. For example, a TypeTag[List[String]] contains all type information, in this case, of type scala.List[String].
  2. scala.reflect.ClassTag. A partial type descriptor of a Scala type. For example, a ClassTag[List[String]] contains only the erased class type information, in this case, of type scala.collection.immutable.List. ClassTags provide access only to the runtime class of a type. Analogous to scala.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]的作用就是步骤类型为Timplicit参数并返回。implicitly的源码如下:

@inline 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.Listdef 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