Manifest和TypeTag是要解决什么问题?
As with other JVM languages, Scala’s types are erased at compile time. This means that if you were to inspect the runtime type of some instance, you might not have access to all type information that the Scala compiler has available at compile time.
由于类型擦除,在编译期存在的类型信息,在编译后,在runtime就丢失了。为了在runtime获得编译期得到的类型信息,需要额外传递信息,所以需要Manifest和TypeTag携带这些信息。
Manifest和TypeTag把这些类型信息从compile time 携带到 runtime.
A TypeTag[T] encapsulates the runtime type representation of some type T.所以,只要使用正确的TypeTag[T],就能在运行期获取T的类型信息。
Manifest是scala2.8引入的一个特质,用于编译器在运行时获取泛型类型的信息。在JVM上,泛型参数类型T在运行时是被“擦拭”掉的,编译器把T当作Object来对待,所以T的具体信息是无法得到的;
为了使得在运行时得到T的信息,scala需要额外通过Manifest来存储T的信息,并作为参数用在方法的运行时上下文。
scala> def max[T : Comparator] (a:T, b:T) = { … }
上面的类型参数声明
T : Comparator
就表示存在一个
Comparator[T]
类型的隐式值。
TypeTag 在import scala.reflect.runtime.universe._路径下
它用来替代Manifest。因为Manifest不能识别path依赖的类型。
class Foo{class Bar} 中的 foo1.bar1 和foo2.bar2, Manifest就会识别为同一类型。而TypeTag不会
而且TypeTag可以用来检查类型参数,使用typeOf[T]
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 Foo))
res68: String = list of foos
上边的typeOf[A]在传入参数为List("String")时,得到结果是java.lang.String
typeOf[T]接受一个类型为TypeTag[T]的隐式参数,所以编译器生成的TypeTag隐式参数会被传给typeOf[T]
有哪些种TypeTag?
There exist three different types of TypeTags:
-
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 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 type scala.collection.immutable.List
. ClassTag
s provide access only to the runtime class of a type. Analogous to scala.reflect.ClassManifest
.
-
scala.reflect.api.TypeTags#WeakTypeTag
. A type descriptor for abstract types (see corresponding subsection below).
如何在运行时获取叁数类型的信息?
首先通过Manifest和TypeTag获取的信息是针对参数类型的,而不是参数。所以一个TypeTag对象是跟一个类型相关的,而不是和一个具体的对象相关的。
那么问题是:
1. 如何让编译器知道哪个类型参数需要生成TypeTag?显式地使用隐式参数,或者在声明泛型参数时使用context bound,这编译器自动生成隐式参数。
2. 如何在代码体中获取TypeTag? 当显式使用隐式参数时,可以直接使用这个隐式参数;当使用context bounds时,使用接受相关隐式参数的方法,比如typeOf。
三种方法:
- via the Methods
typeTag
, classTag
, or weakTypeTag
- Using an Implicit Parameter of Type
TypeTag[T]
, ClassTag[T]
, orWeakTypeTag[T]
- Using a Context bound of a Type Parameter
见http://docs.scala-lang.org/overviews/reflection/typetags-manifests.html