什么是 TypeTag 以及如何使用它? [英] What is a TypeTag and how do I use it?

查看:42
本文介绍了什么是 TypeTag 以及如何使用它?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对 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] = Foo@681e731c.type#Foo$Bar

scala> val ev2 = m(f2)(b2)
warning: there were 2 deprecation warnings; re-run with -deprecation for details
ev2: Manifest[f2.Bar] = Foo@3e50039c.type#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 替代 ClassManifestTypeTag 或多或少是 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 时得到的相同 被调用.当然,也可以同时使用 ClassTagTypeTag:

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]
                                                      ^

有关 TypeTagWeakTypeTag 之间差异的更详细说明,请参阅此问题: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 以及如何使用它?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆