问题描述
我对 TypeTags 的了解只是它们以某种方式取代了 Manifests.互联网上的信息很少,我无法很好地理解这个主题.
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.
因此,如果有人分享了一些有关 TypeTag 的有用材料的链接,包括示例和流行用例,我会很高兴.也欢迎详细解答和解释.
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.
推荐答案
一个 TypeTag
解决了 Scala 的类型在运行时被擦除(类型擦除)的问题.如果我们想做
A TypeTag
solves the problem that Scala's types are erased at runtime (type erasure). If we wanna do
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"
}
我们会收到警告:
<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"
^
为了解决这个问题Manifests介绍了 Scala.但是他们的问题是无法表示很多有用的类型,比如依赖路径的类型:
To solve this problem Manifests were introduced to Scala. But they have the problem not being able to represent a lot of useful types, like path-dependent-types:
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
因此,它们被替换为 TypeTags,它们使用起来更加简单,并且很好地集成到了新的 Reflection API 中.有了它们,我们可以优雅地解决上面关于路径依赖类型的问题:
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:
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:
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).
TypeTag
完全是编译器生成的,这意味着当调用一个需要 TypeTag
的方法时,编译器会创建并填充 TypeTag
代码>.存在三种不同形式的标签:
A TypeTag
is completely compiler-generated, that means that the compiler creates and fills in a TypeTag
when one calls a method expecting such a TypeTag
. There exist three different forms of tags:
ClassTag
替代 ClassManifest
而 TypeTag
或多或少是 Manifest
的替代品.
ClassTag
substitutes ClassManifest
whereas TypeTag
is more or less the replacement for 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$1: 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)
ClassTag
仅提供在运行时创建类型所需的信息(类型已擦除):
ClassTag
provides only the information needed to create types at runtime (which are type erased):
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]
正如你在上面看到的,他们不关心类型擦除,因此如果想要完整"类型,应该使用 TypeTag
:
As one can see above, they don't care about type erasure, therefore if one wants "full" types TypeTag
should be used:
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
正如我们所看到的,TypeTag
的方法 tpe
导致一个完整的 Type
,这与我们在 typeOf 时得到的相同
被调用.当然,也可以同时使用 ClassTag
和 TypeTag
:
As one can see, method tpe
of TypeTag
results in a full Type
, which is the same we get when typeOf
is called. Of course, it is possible to use both, ClassTag
and TypeTag
:
scala> def m[A : ClassTag : TypeTag] = (classTag[A], typeTag[A])
m: [A](implicit evidence$1: scala.reflect.ClassTag[A],↩
implicit evidence$2: 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]])
现在剩下的问题是 WeakTypeTag
的意义是什么?简而言之,TypeTag
代表一个具体类型(这意味着它只允许完全实例化的类型)而 WeakTypeTag
只允许任何类型.大多数时候人们并不关心哪个是什么(这意味着应该使用 TypeTag
),但是例如,当使用应该与泛型类型一起使用的宏时,它们是需要的:
The remaining question now is what is the sense of WeakTypeTag
? In short, TypeTag
represents a concrete type (this means it only allows fully instantiated types) whereas WeakTypeTag
just allows any type. Most of the time one does not care which is what (which means TypeTag
should be used), but for example, when macros are used which should work with generic types they are needed:
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
???
}
}
如果将 WeakTypeTag
替换为 TypeTag
,则会抛出错误:
If one replaces WeakTypeTag
with TypeTag
an error is thrown:
<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$1: 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]
^
有关 TypeTag
和 WeakTypeTag
之间差异的更详细说明,请参阅此问题:Scala 宏:无法从具有未解析类型参数的类型 T 创建 TypeTag"
For a more detailed explanation about the differences between TypeTag
and WeakTypeTag
see this question: Scala Macros: "cannot create TypeTag from a type T having unresolved type parameters"
Scala 的官方文档站点还包含一个反射指南.
The official documentation site of Scala also contains a guide for Reflection.
这篇关于什么是 TypeTag 以及如何使用它?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!