我正在使用 scala 2.10 的工具箱 api 在运行时从 AST 编译和构造类。我有一个特性的 AST,我想编译它并将其混合到未来的类定义中,但无法获得将其应用于我的类的任何句柄。
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> import scala.tools.reflect.ToolBox
import scala.tools.reflect.ToolBox
scala> val tb = scala.reflect.runtime.currentMirror.mkToolBox()
tb: scala.tools.reflect.ToolBox[reflect.runtime.universe.type] = scala.tools.reflect.ToolBoxFactory$ToolBoxImpl@7469d79a
scala> val ast_trait = tb.parse("trait T")//My AST is manually constructed starting with ClassDef
ast_trait: tb.u.Tree = abstract trait T extends scala.AnyRef
//compile and execute the tree
scala> tb.eval(ast_trait)
res0: Any = ()
//attempt to define a class that
scala> tb.eval(tb.parse("class C extends T"))implements T. In my code this is a constructed AST
scala.tools.reflect.ToolBoxError: reflective compilation has failed:
not found: type T
/*stack trace*/
这会失败,因为当使用工具箱编译类/特征时,它们会被包装并命名为 __wrapper$[wrapCount]$[uuid]$[name]。
问题出现了如何获得要与类定义一起使用的已定义特征的句柄。
显而易见的解决方案是scala.reflect.runtime.universe.Type
通过typeOf[T]
.
//Obtain an AST via tb.parse
scala> val ast_type = tb.parse("scala.reflect.runtime.universe.typeOf[T]")
ast_type: tb.u.Tree = scala.reflect.runtime.universe.typeOf[T]
//eval the trait defintion and typeOf in the same block
scala> tb.eval(Block(ast_trait, ast_type))
warning: there were 1 deprecation warning(s); re-run with -deprecation for details
scala.tools.reflect.ToolBoxError: reflective compilation has failed:
No TypeTag available for T
not enough arguments for method typeOf: (implicit ttag: reflect.runtime.universe.TypeTag[T])reflect.runtime.universe.Type.
Unspecified value parameter ttag.
/*stack trace*/
我不完全确定为什么会失败,但我之前没有成功地以任何形式使用隐式类型标签和工具箱编译。
//A simpler all in one example
scala> tb.eval(tb.parse("trait T; scala.reflect.runtime.universe.typeOf[T]"))
scala.tools.reflect.ToolBoxError: reflective compilation has failed:
No TypeTag available for T
not enough arguments for method typeOf: (implicit ttag: reflect.runtime.universe.TypeTag[T])reflect.runtime.universe.Type.
Unspecified value parameter ttag.
/*stack trace*/
有没有办法禁用包装,以便使用 AST 中指定的名称定义特征?
如果没有,如何获得特征的全名?
更新: using 的建议classOf
就像一个魅力,直到特性实际应用于一个类。
scala> val mirror = scala.reflect.runtime.currentMirror
scala> val clazz = tb.eval(tb.parse("trait T{def m = 0}; classOf[T]")).asInstanceOf[Class[_]]
clazz: Class[_] = interface __wrapper$5$36e5027b48a44570a47be32d536aa67d.__wrapper$5$36e5027b48a44570a47be32d536aa67d$T$1
scala> val tpe = mirror.classSymbol(clazz)
tpe: reflect.runtime.universe.ClassSymbol = trait T$1
scala> val tree = ClassDef(Modifiers(), "C", List(), Template(
| List(Ident(tpe)),
| emptyValDef,
| List()))
tree: reflect.runtime.universe.ClassDef = class C extends T$1
scala> tb.eval(tree)
scala.tools.reflect.ToolBoxError: reflective compilation has failed:
class C needs to be abstract, since method m in trait T$1 of type ()Int is not defined
/*stack trace*/
似乎特征的实现正在丢失,它只是作为一个接口应用(这是由 classOf 返回的 java 类定义的)。
我假设工具箱的包装器正在使用新的 uuid 重命名用于实现 trait 的 java 类(通常具有相同的类名,后跟“$”),然后编译器无法找到它并假设它只是一个接口。
如何将具有实现的特征应用于类?