程序员人生 网站导航

Scala之TypeTags and Manifests

栏目:php教程时间:2016-11-03 08:45:35

注:本文为译文,原文是Scala官方文档中的1篇,这里翻译出来作为本系列的1篇文章。原文链接: http://docs.scala-lang.org/overviews/reflection/typetags-manifests.html。 本文原文出处: http://blog.csdn.net/bluishglc/article/details/52596696 严禁任何情势的转载,否则将拜托CSDN官方保护权益!

  • 获得1个TypeTag
    • 通过typeTagclassTag或weakTypeTag方法
    • 使用TypeTagTClassTagT或WeakTypeTagT的1个隐式参数
    • 使用类型参数的Context Bound
  • WeakTypeTags
  • TypeTags和Manifests

犹如所有其他的JVM语言,Scala(在范型中)的类型信息在编译期都被擦除,这意味着如果你在运行时去检测1些实例的类型,你可能没法获得全部的类型信息,即便Scala的编译器在编译时期都知道这些信息。

像scala.reflect.Manifest1样, TypeTags可以被认为是携带了编译期全部类型信息到运行期的对象,例如,TypeTag[T]封装了在编译时期肯定的T的实际类型信息,而这些信息可以在运行时期使用(为表述更清晰,本句翻译在原文基础上做了调剂),你要记住,TypeTags可以视作Scala 2.10之前的Manifest类的1个更加丰富的替换者,它完全和Scala的反射系统继承在了1起。

Scala有3种不同类型的TypeTags

  1. scala.reflect.api.TypeTags#TypeTag,Scala类型的全类型描写符,例如,TypeTag[List[String]]包括了所有的类型信息,也就是scala.List[String](译者注:也就是除List类本身,它的范型String类型也被保存并可以获得)。
  2. scala.reflect.ClassTag,Scala类型的部份类型描写符,例如,ClassTag[List[String]]只包括被擦除的的类型信息,也就是scala.collection.immutable.List(译者注:也就是只有List类本身,它的类型参数String是不会被保存的)。ClassTag只提供了访问1个类型的运行时的类型信息,类似于scala.reflect.ClassManifest。
  3. scala.reflect.api.TypeTags#WeakTypeTag,抽象类类型的描写符(参考后文)。

获得1个TypeTag

犹如Manifest,TypeTag总是由编译器生成的,可以通过3种方式获得。

通过typeTag,classTag或weakTypeTag方法

你可以通过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]的实例(译者注:在编译期由编译器自动完成)。

使用TypeTag[T],ClassTag[T]或WeakTypeTag[T]的1个隐式参数

像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)

使用类型参数的Context Bound

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)

WeakTypeTags

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和Manifests

宽泛地说,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.reflect.OptManifest不再被支持,由于Tag类可以具体化到任意类型,所以它们总是可用的。
  • 没有scala.reflect.AnyValManifest的等价物。相应地,你可以用base Tag比较它们的Tag以便找出是不是它表示的是1个primitive value class。另外,它还可以简单的使用.tpe.typeSymbol.isPrimitiveValueClass
  • Manifest伴生对象中定义的工厂方法也没有替换物
  • 某些 manifest 的操作如(i.e., <:<, >:> and typeArguments)都不再被支持。

在Scala 2.10里,scala.reflect.ClassManifests不再被推荐使用!scala.reflect.Manifest也将被TypeTags和ClassTag所代表的风格所取代。

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐