Scala之TypeTags and Manifests

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

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

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

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只提供了访问一个类型的运行时的类型信息,类似于scala.reflect.ClassManifest。
  3. scala.reflect.api.TypeTags#WeakTypeTag,抽象类类型的描述符(参考后文)。

获取一个TypeTag

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

通过typeTag,classTag或weakTypeTag方法

你可以通过typeTag方法直接获取某个特定类型的TypeTag,typeTag方法在Universe。

例如, 获取Int型的一个TypeTag:

import scala.reflect.runtime.universe._
val tt = typeTag[Int]

或者获取String型的一个ClassTag:

import scala.reflect._
val ct = classTag[String]

这些方法基于给定的类型参数T构建了TypeTag[T] 或ClassTag[T]的实例(译者注:在编译期由编译器自动完成)。

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

像Manifest一样,你实际上可以请求编译器去生成一个TypeTag, 这可以通过指定一个TypeTag[T]的隐式的“迹象”(evidence)参数来实现。如果编译器在编译期间没能找到匹配的隐式值,编译器就会自动创建一个TypeTag[T]的实例(译者注:当然,这时类型T已经确定)。

注意:在方法和类上使用TypeTag[T],ClassTag[T]或WeakTypeTag[T]的一个隐式参数的做法是很常见的。

例如,我们可以写一个接受任何类型做参数的方法,然后通过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")
}

在这里,我们写了一个范型方法paramInfo,同时提供了一个隐式参数(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 本身是一个List,42是简单类型,没有类型参数,所以是一个空的List,List(1, 2)显然是一个List[Int],所以它的类型参数是Int)

使用类型参数的Context Bound

一种更加简单地实现上述目标的做法是使用一个类型参数的Context Bound。不同于提供一个独立的隐式参数,你可以像下面这样把TypeTag简单的加到类型参数列表中:

def myMethod[T: TypeTag] = ...

基于给定的上下文边界[T: TypeTag],编译器会简单地生成一个TypeTag[T]的隐式参数,然后把方法重写成像前一个带隐式参数的例子那样。

上面使用上下文边界的例子会被(编译器)重写为:

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]。不像一个常规的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是相一致的。而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以便找出是否它表示的是一个primitive value class。另外,它还可以简单的使用.tpe.typeSymbol.isPrimitiveValueClass
  • Manifest伴生对象中定义的工厂方法也没有替换物
  • 某些 manifest 的操作如(i.e., <:<, >:> and typeArguments)都不再被支持。

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

bluishglc CSDN认证博客专家 CSDN博客专家
架构师,CSDN博客专家,14年IT系统开发和架构经验,对大数据、企业级应用架构、SaaS、分布式存储和领域驱动设计有丰富的实践经验。对Hadoop/Spark 生态系统有深入和广泛的研究,参与过Hadoop商业发行版的开发,目前负责企业数据中台的架构设计和开发工作,热衷函数式编程,著有《大数据平台架构与原型实现:数据中台建设实战》https://item.jd.com/12677623.html 一书。
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页