Scala:什么是 TypeTag,我该如何使用它?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12218641/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Scala: What is a TypeTag and how do I use it?
提问by Sergey Weiss
All I know about TypeTags is that they somehow replaced Manifests. Information on the Internet is scarce and doesn't provide me with a good sense of the subject.
我对 TypeTags 的了解只是它们以某种方式取代了 Manifests。Internet 上的信息很少,而且我无法很好地了解该主题。
So I'd be happy if someone shared a link to some useful materials on TypeTags including examples and popular use-cases. Detailed answers and explanations are also welcome.
因此,如果有人在 TypeTags 上分享了一些有用材料的链接,包括示例和流行用例,我会很高兴。也欢迎详细的回答和解释。
回答by kiritsuku
A TypeTagsolves the problem that Scala's types are erased at runtime (type erasure). If we wanna do
ATypeTag解决了Scala的类型在运行时被擦除的问题(类型擦除)。如果我们想做
class Foo
class Bar extends Foo
def meth[A](xs: List[A]) = xs match {
case _: List[String] => "list of strings"
case _: List[Foo] => "list of foos"
}
we will get warnings:
我们会收到警告:
<console>:23: warning: non-variable type argument String in type pattern List[String]?
is unchecked since it is eliminated by erasure
case _: List[String] => "list of strings"
^
<console>:24: warning: non-variable type argument Foo in type pattern List[Foo]?
is unchecked since it is eliminated by erasure
case _: List[Foo] => "list of foos"
^
To solve this problem Manifestswere introduced to Scala. But they have the problem not being able to represent a lot of useful types, like path-dependent-types:
为了解决这个问题,Scala 引入了清单。但是他们的问题是无法表示很多有用的类型,比如依赖路径的类型:
scala> class Foo{class Bar}
defined class Foo
scala> def m(f: Foo)(b: f.Bar)(implicit ev: Manifest[f.Bar]) = ev
warning: there were 2 deprecation warnings; re-run with -deprecation for details
m: (f: Foo)(b: f.Bar)(implicit ev: Manifest[f.Bar])Manifest[f.Bar]
scala> val f1 = new Foo;val b1 = new f1.Bar
f1: Foo = Foo@681e731c
b1: f1.Bar = Foo$Bar@271768ab
scala> val f2 = new Foo;val b2 = new f2.Bar
f2: Foo = Foo@3e50039c
b2: f2.Bar = Foo$Bar@771d16b9
scala> val ev1 = m(f1)(b1)
warning: there were 2 deprecation warnings; re-run with -deprecation for details
ev1: Manifest[f1.Bar] = [email protected]#Foo$Bar
scala> val ev2 = m(f2)(b2)
warning: there were 2 deprecation warnings; re-run with -deprecation for details
ev2: Manifest[f2.Bar] = [email protected]#Foo$Bar
scala> ev1 == ev2 // they should be different, thus the result is wrong
res28: Boolean = true
Thus, they are replaced by TypeTags, which are both much simpler to use and well integrated into the new Reflection API. With them we can solve the problem above about path-dependent-types elegantly:
因此,它们被TypeTags取代,后者使用起来更加简单,并且很好地集成到了新的 Reflection API 中。有了它们,我们可以优雅地解决上面关于路径依赖类型的问题:
scala> def m(f: Foo)(b: f.Bar)(implicit ev: TypeTag[f.Bar]) = ev
m: (f: Foo)(b: f.Bar)(implicit ev: reflect.runtime.universe.TypeTag[f.Bar])?
reflect.runtime.universe.TypeTag[f.Bar]
scala> val ev1 = m(f1)(b1)
ev1: reflect.runtime.universe.TypeTag[f1.Bar] = TypeTag[f1.Bar]
scala> val ev2 = m(f2)(b2)
ev2: reflect.runtime.universe.TypeTag[f2.Bar] = TypeTag[f2.Bar]
scala> ev1 == ev2 // the result is correct, the type tags are different
res30: Boolean = false
scala> ev1.tpe =:= ev2.tpe // this result is correct, too
res31: Boolean = false
They are also easy to use to check type parameters:
它们也很容易用于检查类型参数:
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 Bar))
res68: String = list of foos
At this point, it is extremely important to understand to use =:=(type equality) and <:<(subtype relation) for equality checks. Do never use ==or !=, unless you absolutely know what you do:
在这一点上,理解使用=:=(类型相等)和<:<(子类型关系)进行相等检查是非常重要的。不要使用==or !=,除非你完全知道你在做什么:
scala> typeOf[List[java.lang.String]] =:= typeOf[List[Predef.String]]
res71: Boolean = true
scala> typeOf[List[java.lang.String]] == typeOf[List[Predef.String]]
res72: Boolean = false
The latter checks for structural equality, which often is not what should be done because it doesn't care about things such as prefixes (like in the example).
后者检查结构相等性,这通常不是应该做的,因为它不关心诸如前缀之类的事情(如示例中所示)。
A TypeTagis completely compiler-generated, that means that the compiler creates and fills in a TypeTagwhen one calls a method expecting such a TypeTag. There exist three different forms of tags:
ATypeTag是完全编译器生成的,这意味着编译器会在TypeTag调用需要这样TypeTag. 存在三种不同形式的标签:
ClassTagsubstitutes ClassManifestwhereas TypeTagis more or less the replacement for Manifest.
ClassTag替代品ClassManifest而TypeTag或多或少是 的替代品Manifest。
The former allows to fully work with generic arrays:
前者允许完全使用通用数组:
scala> import scala.reflect._
import scala.reflect._
scala> def createArr[A](seq: A*) = Array[A](seq: _*)
<console>:22: error: No ClassTag available for A
def createArr[A](seq: A*) = Array[A](seq: _*)
^
scala> def createArr[A : ClassTag](seq: A*) = Array[A](seq: _*)
createArr: [A](seq: A*)(implicit evidence: scala.reflect.ClassTag[A])Array[A]
scala> createArr(1,2,3)
res78: Array[Int] = Array(1, 2, 3)
scala> createArr("a","b","c")
res79: Array[String] = Array(a, b, c)
ClassTagprovides only the information needed to create types at runtime (which are type erased):
ClassTag仅提供在运行时创建类型所需的信息(类型已擦除):
scala> classTag[Int]
res99: scala.reflect.ClassTag[Int] = ClassTag[int]
scala> classTag[Int].runtimeClass
res100: Class[_] = int
scala> classTag[Int].newArray(3)
res101: Array[Int] = Array(0, 0, 0)
scala> classTag[List[Int]]
res104: scala.reflect.ClassTag[List[Int]] =?
ClassTag[class scala.collection.immutable.List]
As one can see above, they don't care about type erasure, therefore if one wants "full" types TypeTagshould be used:
正如你在上面看到的,他们不关心类型擦除,因此如果想要“完整”类型TypeTag应该使用:
scala> typeTag[List[Int]]
res105: reflect.runtime.universe.TypeTag[List[Int]] = TypeTag[scala.List[Int]]
scala> typeTag[List[Int]].tpe
res107: reflect.runtime.universe.Type = scala.List[Int]
scala> typeOf[List[Int]]
res108: reflect.runtime.universe.Type = scala.List[Int]
scala> res107 =:= res108
res109: Boolean = true
As one can see, method tpeof TypeTagresults in a full Type, which is the same we get when typeOfis called. Of course, it is possible to use both, ClassTagand TypeTag:
如您所见, method tpeof 会TypeTag产生一个 full Type,这与我们在typeOf被调用时得到的结果相同。当然,可以同时使用,ClassTag和TypeTag:
scala> def m[A : ClassTag : TypeTag] = (classTag[A], typeTag[A])
m: [A](implicit evidence: scala.reflect.ClassTag[A],?
implicit evidence: reflect.runtime.universe.TypeTag[A])?
(scala.reflect.ClassTag[A], reflect.runtime.universe.TypeTag[A])
scala> m[List[Int]]
res36: (scala.reflect.ClassTag[List[Int]],?
reflect.runtime.universe.TypeTag[List[Int]]) =?
(scala.collection.immutable.List,TypeTag[scala.List[Int]])
The remaining question now is what is the sense of WeakTypeTag? In short, TypeTagrepresents a concrete type (this means it only allows fully instantiated types) whereas WeakTypeTagjust allows any type. Most of the time one does not care which is what (which means TypeTagshould be used), but for example, when macros are used which should work with generic types they are needed:
现在剩下的问题是什么是意义WeakTypeTag?简而言之,TypeTag代表一个具体类型(这意味着它只允许完全实例化的类型)而WeakTypeTag只允许任何类型。大多数时候人们并不关心哪个是什么(这意味着TypeTag应该使用),但是例如,当使用应该与泛型类型一起使用的宏时,它们是需要的:
object Macro {
import language.experimental.macros
import scala.reflect.macros.Context
def anymacro[A](expr: A): String = macro __anymacro[A]
def __anymacro[A : c.WeakTypeTag](c: Context)(expr: c.Expr[A]): c.Expr[A] = {
// to get a Type for A the c.WeakTypeTag context bound must be added
val aType = implicitly[c.WeakTypeTag[A]].tpe
???
}
}
If one replaces WeakTypeTagwith TypeTagan error is thrown:
如果一个替换WeakTypeTag为TypeTag一个错误被抛出:
<console>:17: error: macro implementation has wrong shape:
required: (c: scala.reflect.macros.Context)(expr: c.Expr[A]): c.Expr[String]
found : (c: scala.reflect.macros.Context)(expr: c.Expr[A])(implicit evidence: c.TypeTag[A]): c.Expr[A]
macro implementations cannot have implicit parameters other than WeakTypeTag evidences
def anymacro[A](expr: A): String = macro __anymacro[A]
^
For a more detailed explanation about the differences between TypeTagand WeakTypeTagsee this question: Scala Macros: “cannot create TypeTag from a type T having unresolved type parameters”
有关两者之间差异的更详细说明TypeTag,WeakTypeTag请参阅以下问题:Scala 宏:“无法从具有未解析类型参数的类型 T 创建 TypeTag”
The official documentation site of Scala also contains a guide for Reflection.
Scala 的官方文档站点还包含Reflection的指南。

![scala 如何在选项 [String] 中设置字符串?](/res/img/loading.gif)