如何手动创建TypeTag?
我感兴趣的是手动创建TypeTag(因为2.10M5):如何手动创建TypeTag?
object X {
import reflect.runtime.universe._
def tt[A : TypeTag](a: A) = typeTag[A] // how to do this manually?
val t = tt(List("")(_))
}
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
}
}
这似乎是完全编译魔法,因为类型是固定的。不过有没有办法做到这一点手动?
在M3中,您可以使用一种非常简单的方式创建类型标签,例如:TypeTag[Int](TypeRef(<scala package>, <symbol of scala.Int>, Nil))
。它基本上意味着一旦创建了一个类型标签,它就永远被绑定到某个类加载器(也就是上面例子中用于加载scala符号的类)。
那时很好,因为我们认为我们可以有一个适合所有类加载器的万能镜。这很方便,因为您只需编写implicitly[TypeTag[T]]
,编译器将使用该全局镜像实例化您请求的类型。
不幸的是,后来,根据反馈意见,我们意识到这不会起作用,并且我们需要多个镜像 - 每个镜像都有自己的类加载器。
然后很明显,类型标签需要灵活,因为一旦你编写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类型系统在函数和依赖类型方法之间的边界的一个不幸的限制。
函数定义
def tt[A : TypeTag](a: A) = typeTag[A]
是写
def tt(a: A)(implicit tag: TypeTag[A]) = tag
的只是另一种方式,这意味着正由编译器隐式创建一个标签的实例。这背后的原因是Scala编译器通过硬编码擦除类型信息来解决JVM的类型擦除问题。
如果您不熟悉类型擦除问题,那就是JVM不存储类型参数信息,例如,类型Seq[Set[Int]]
将被JVM看作Seq[_]
,并且将无法你可以通过反射找出遗漏的类型信息。
我不认为这个答案是完全正确的,因为有一种方法可以手动创建舱单这是由编译器自动创建的,太:'新的舱单[INT] {高清擦除= classOf [INT]} '(这在2.10中不起作用) – sschaef 2012-07-15 21:36:12
我不确定我是否理解你要在这里实现的目标。当然,你可以手动实例化一个TypeTag类,但它的唯一目的是在编译器后面实例化,这会生成与案例相关的信息。如果无论你试图模仿它的目的是什么,那么你的反编译的'private [this] val t:reflect.runtime.universe.TypeTag'声明就完全显示了你如何做到这一点,但是这不是这个类的目的使用。 – 2012-07-15 21:51:39
用于此的用例可能是使用Scala方法,该方法需要Java端的TypeTag。我想:也许有比编译器更容易/另一种方式。 – sschaef 2012-07-15 22:19:34
基于Get TypeTag[A] from Class[A]:
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]
}
})
}
例如,这可以用来获得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)
}
这个作品在斯卡拉2.10.2:
scala> inside(typeTag[(Int, Double)])
res0: (reflect.runtime.universe.TypeTag[Int], reflect.runtime.universe.TypeTag[Double]) = (TypeTag[Int],TypeTag[Double])
与特定绑定的限制可能不是问题,只要您没有多个ClassLoader
即可。
这正是我需要的。 – 2014-11-13 00:25:34
此方法失去了标记被序列化的能力:java.io.NotSerializableException:scala.reflect.runtime.JavaMirrors $ JavaMirror – user48956 2016-08-23 01:58:42
我在Scala 2.10上,所以我从来没有看到可序列化的TypeTags。一定很棒! :) – 2016-08-23 09:32:16
目前,有三种方式可以手动创建支持泛型的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[_]]
}
如果你需要一个序列化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]
}
}
}
这第二个模式下创建一个类型树并获取标志着TypeTag
,也就是说,如果你的目标是List[String]
,它会被转换为具体的类型scala.collection.immutable.List[String]
,因为scala.List
只是一个类型别名。这实际上是从typeTag[List[String]]
预期的行为。
最后一种方法是手动创建Type
并使用它创建TypeTag
。这种方法容易出错,只有有限的类型检查,并且使用内部API。然而,这是手动获取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]
}
})
}
我看到,Scala Reflection比我目前能想到的要复杂得多。我仍然需要花费很多时间,直到得到正在发生的事情... – sschaef 2012-07-16 12:50:05
你能否详细说明你的用例?动态构建类型标记(以及因此的Scala类型),特别是从Java中构建类型标记,看起来非常硬。也许类标签(只携带Java类,而不是Scala类型)就足够了? – 2012-07-16 14:44:50
我没有用例;)我想知道事情是如何工作的,以及如何使用它/用于以后使用... – sschaef 2012-07-16 14:51:17