我有兴趣手动创建一个TypeTag(从2.10M5开始):
object X {
import reflect.runtime.universe._
def tt[A : TypeTag](a: A) = typeTag[A] // how to do this manually?
val t = tt(List("")(_))
}
Run Code Online (Sandbox Code Playgroud)
scalac -Xprint:typer <file>.scala
结果是
package <empty> {
object X extends scala.AnyRef {
def <init>(): X.type = {
X.super.<init>();
()
};
import scala.reflect.runtime.`package`.universe._;
def tt[A >: Nothing <: Any](a: A)(implicit evidence$1: reflect.runtime.universe.TypeTag[A]): reflect.runtime.universe.TypeTag[A] = scala.reflect.runtime.`package`.universe.typeTag[A](evidence$1);
private[this] val t: reflect.runtime.universe.TypeTag[Int => String] = X.this.tt[Int => String](((x$1: Int) => immutable.this.List.apply[String]("").apply(x$1)))({
val $u: reflect.runtime.universe.type = scala.this.reflect.runtime.`package`.universe;
val $m: $u.Mirror = scala.this.reflect.runtime.`package`.universe.runtimeMirror(this.getClass().getClassLoader());
$u.TypeTag.apply[Int => String]($m, {
final class $typecreator1 extends TypeCreator {
def <init>(): $typecreator1 = {
$typecreator1.super.<init>();
()
};
def apply[U >: Nothing <: scala.reflect.base.Universe with Singleton]($m$untyped: scala.reflect.base.MirrorOf[U]): U#Type = {
val $u: U = $m$untyped.universe;
val $m: $u.Mirror = $m$untyped.asInstanceOf[$u.Mirror];
$u.TypeRef.apply($u.ThisType.apply($m.staticModule("scala").asModuleSymbol.moduleClass), $m.staticClass("scala.Function1"), scala.collection.immutable.List.apply[$u.Type]($m.staticClass("scala.Int").asTypeSymbol.asTypeConstructor, $m.staticClass("java.lang.String").asTypeSymbol.asTypeConstructor))
}
};
new $typecreator1()
})
});
<stable> <accessor> def t: reflect.runtime.universe.TypeTag[Int => String] = X.this.t
}
}
Run Code Online (Sandbox Code Playgroud)
它似乎完全是编译器魔术,因为类型是硬编码的.不过有没有办法手动执行此操作?
Eug*_*ako 10
在M3中,您可以以非常简单的方式创建类型标记,例如:TypeTag[Int](TypeRef(<scala package>, <symbol of scala.Int>, Nil))
.它基本上意味着一旦创建了一个类型标记,它就会永远绑定到某个类加载器(即上面示例中用于加载scala.Int符号的类加载器).
当时很好,因为我们认为我们可以拥有一个适合所有类加载器的一体适用的镜像.这很方便,因为您可以编写implicitly[TypeTag[T]]
,编译器将使用该全局镜像来实例化您请求的Type.
不幸的是,根据反馈,我们意识到这不会起作用,我们需要多个镜像 - 每个镜像都有自己的类加载器.
然后很明显,类型标签需要灵活,因为一旦你编写,implicitly[TypeTag[T]]
编译器就没有足够的信息你想要使用哪个类加载器.基本上有两种选择:1)使类型标记路径依赖于镜像(因此每次从编译器请求类型标记时都会强制显式提供镜像),2)将类型标记转换为类型工厂,能够在任何镜子中实例化自己 长话短说,第一个选项没有用,所以我们就在这里.
因此,如https://github.com/scala/scala/blob/master/src/library/scala/reflect/base/TypeTags.scala#L143中所述,目前类型标签需要以相当迂回的方式创建.您调用随附的工厂方法TypeTag
并提供两个参数:1)默认镜像,其中将实例化类型标记,2)可以在任意镜像中实例化类型标记的类型工厂.这基本上就是编译器在你上面附带的打印输出中所做的.
为什么我打电话给这个环岛?因为要提供类型工厂,您需要手动子类化scala.reflect.base.TypeFactory
该类.这是Scala类型系统在函数和依赖类型方法之间的边界上的一个不幸的限制.
Dan*_*bos 10
import scala.reflect.runtime.universe._
def typeToTypeTag[T](
tpe: Type,
mirror: reflect.api.Mirror[reflect.runtime.universe.type]
): TypeTag[T] = {
TypeTag(mirror, new reflect.api.TypeCreator {
def apply[U <: reflect.api.Universe with Singleton](m: reflect.api.Mirror[U]) = {
assert(m eq mirror, s"TypeTag[$tpe] defined in $mirror cannot be migrated to $m.")
tpe.asInstanceOf[U#Type]
}
})
}
Run Code Online (Sandbox Code Playgroud)
例如,这可用于获取TypeTag
另一部分TypeTag
:
def inside[A, B](tag: TypeTag[(A, B)]): (TypeTag[A], TypeTag[B]) = {
val tpes = tag.tpe.asInstanceOf[TypeRefApi].args
val tagA = typeToTypeTag[A](tpes(0), tag.mirror)
val tagB = typeToTypeTag[B](tpes(1), tag.mirror)
return (tagA, tagB)
}
Run Code Online (Sandbox Code Playgroud)
这适用于Scala 2.10.2:
scala> inside(typeTag[(Int, Double)])
res0: (reflect.runtime.universe.TypeTag[Int], reflect.runtime.universe.TypeTag[Double]) = (TypeTag[Int],TypeTag[Double])
Run Code Online (Sandbox Code Playgroud)
Mirror
只要你没有多个ClassLoader
s ,绑定到特定的限制可能不是问题.
功能定义
def tt[A : TypeTag](a: A) = typeTag[A]
Run Code Online (Sandbox Code Playgroud)
只是另一种写作方式
def tt(a: A)(implicit tag: TypeTag[A]) = tag
Run Code Online (Sandbox Code Playgroud)
这意味着标记的实例是由编译器隐式创建的.这背后的原因是Scala编译器通过硬编码其他擦除类型信息来解决JVM的类型擦除问题.
如果您不熟悉类型擦除问题,那就是JVM不存储类型参数信息,例如JVM会Seq[Set[Int]]
简单地看到类型Seq[_]
,并且您无法找到丢失的内容用反射键入信息.
目前,有三种方法可以手动创建TypeTag
支持泛型的方法.前两个取决于scala-compiler
,并且与它们一样,您可以像编译时一样进行类型检查,因为它是一个实际的代码编译:
import scala.reflect.runtime.universe._
import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox
val toolbox = currentMirror.mkToolBox()
def createTypeTag(tp: String): TypeTag[_] = {
val ttree = toolbox.parse(s"scala.reflect.runtime.universe.typeTag[$tp]")
toolbox.eval(ttree).asInstanceOf[TypeTag[_]]
}
Run Code Online (Sandbox Code Playgroud)
如果您需要可序列化TypeTag
并且性能不是您主要关心的问题,那么第一种方法就是最简洁的方法.OTOH,如果你的用例需要非常高效,请注意即时编译可能需要几秒钟才能完成.
第二种方法表现得更好:
def createTypeTag(tp: String): TypeTag[_] = {
val ttagCall = s"scala.reflect.runtime.universe.typeTag[$tp]"
val tpe = toolbox.typecheck(toolbox.parse(ttagCall), toolbox.TYPEmode).tpe.resultType.typeArgs.head
TypeTag(currentMirror, new reflect.api.TypeCreator {
def apply[U <: reflect.api.Universe with Singleton](m: reflect.api.Mirror[U]) = {
assert(m eq mirror, s"TypeTag[$tpe] defined in $mirror cannot be migrated to $m.")
tpe.asInstanceOf[U#Type]
}
}
}
Run Code Online (Sandbox Code Playgroud)
第二种模式创建一个类型化的树并获取标记在其中的具体类型TypeTag
,即,如果您的目标是a List[String]
,它将被翻译为scala.collection.immutable.List[String]
,因为scala.List
它只是一个类型别名.这实际上是预期的行为typeTag[List[String]]
.
最后一种方法是Type
手动创建并使用它来创建TypeTag
.这种方法容易出错,类型检查有限,并且它使用内部API.然而,这是手动获取a的最快方法TypeTag
.
def createTypeTag(tp: String): TypeTag[_] = {
val typs = // ... manipulate the string to extract type and parameters
val typSym = currentMirror.staticClass(typs[0])
val paramSym = currentMirror.staticClass(typs[1])
val tpe = universe.internal.typeRef(NoPrefix, typSym, List(paramSym.selfType))
val ttag = TypeTag(currentMirror, new TypeCreator {
override def apply[U <: Universe with Singleton](m: Mirror[U]): U#Type = {
assert(m == currentMirror, s"TypeTag[$tpe] defined in $currentMirror cannot be migrated to $m.")
tpe.asInstanceOf[U#Type]
}
})
}
Run Code Online (Sandbox Code Playgroud)