【scala关键字系列】TypeTags Manifests

本文介绍了Scala中的TypeTags和Manifests概念,重点讲解了如何通过typeTag、classTag、weakTypeTag获取类型标签,以及使用隐式参数和上下文边界获取类型信息。同时提到了WeakTypeTags的特点以及TypeTags与Manifests之间的关系和替代性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【scala关键字系列】TypeTags Manifests


在Scala中,类型在运行时被擦除,这意味着如果你检查某个实例的运行时类型,可能无法获取编译时Scala编译器可用的所有类型信息。

scala.reflect.Manifest类似,**TypeTags可以看作是将所有可用的类型信息从编译时传递到运行时的对象。**例如,TypeTag[T]封装了某个编译时类型T的运行时类型表示。需要注意的是,TypeTags应该被视为2.10之前Manifest的更丰富的替代品,并且与Scala反射完全集成。

存在三种不同类型的TypeTags

  • scala.reflect.api.TypeTags#TypeTag:Scala类型的完整类型描述符。例如,TypeTag[List[String]]包含了List[String]类型的所有类型信息。

  • scala.reflect.ClassTag:Scala类型的部分类型描述符。例如,ClassTag[List[String]]仅包含已擦除的类类型信息,即scala.collection.immutable.ListClassTags只提供对类型的运行时类的访问。类似于scala.reflect.ClassManifest

  • scala.reflect.api.TypeTags#WeakTypeTag:抽象类型的类型描述符(请参见下面的相应小节)。

获取TypeTag

Manifests一样,TypeTags始终由编译器生成,并且可以通过三种方式获得。

1. 通过typeTagclassTagweakTypeTag方法

可以通过Universe上可用的typeTag方法直接获得特定类型的TypeTag

例如,要获得表示IntTypeTag,可以这样做:

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

同样地,要获得表示StringClassTag,可以这样做:

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

每个方法都构造了一个TypeTag[T]ClassTag[T],其中T为给定的类型参数。

2. 使用隐式参数TypeTag[T]ClassTag[T]WeakTypeTag[T]

Manifests类似,可以在方法或类中指定一个隐式证据参数,以请求编译器生成TypeTag。只需将类型参数列表中的一个隐式参数的类型设为TypeTag[T]。如果编译器在隐式搜索期间找不到匹配的隐式值,它将自动生成一个TypeTag[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,参数化为T,并提供了一个隐式参数(implicit tag: TypeTag[T])。我们可以直接使用TypeTagtpe方法访问tag所代表的类型(Type)。

然后,我们可以这样使用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)

3. 使用类型参数的上下文边界

以更简洁的方式实现与上述完全相同的效果是使用类型参数的上下文边界。不需要提供单独的隐式参数,只需在类型参数列表中包含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]中。

继续上面的示例:

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对应于scala.reflect.ClassManifestscala.reflect.api.TypeTags#TypeTag在很大程度上对应于scala.reflect.Manifest,其他2.10之前的Manifest类型没有与2.10的“Tag”类型直接对应。

  • scala.reflect.OptManifest不受支持。这是因为Tags可以表示任意类型,因此它们始终可用。

  • scala.reflect.AnyValManifest没有等效物。相反,可以将标签与基本标签(定义在相应的伴生对象中)进行比较,以确定它是否表示原始值类。此外,还可以简单地使用<tag>.tpe.typeSymbol.isPrimitiveValueClass

  • Manifest伴生对象中定义的工厂方法没有替代方案。相反,可以使用Java(用于类)和Scala(用于类型)提供的反射API生成相应的类型。

  • 某些manifest操作(例如,<:<>:>typeArguments)不受支持。可以使用Java(用于类)和Scala(用于类型)提供的反射API来代替。

在Scala 2.10中,scala.reflect.ClassManifest已被弃用,并计划在即将发布的版本中将scala.reflect.Manifest弃用,以便使用Tags。因此,建议将基于Manifest的API迁移到使用Tags。

参考链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BigDataMLApplication

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值