在Scala 2.10中如何通过字符串生成类并实例化它

45
在Scala 2.10中,我如何从字符串生成一个类(可能使用Toolbox API),以便稍后使用Scala反射进行实例化?
1个回答

54

关于编译工具箱,它们只能运行表达式并返回值,而不能返回生成的类或文件/字节数组的编译结果。

但是你仍然可以实现你想要的功能,因为在Scala中使用隐式值非常容易从类型级别转到值级别:

编辑。 在2.10.0-RC1中,ToolBox的某些方法已更名。 parseExpr现在称为parse,而runExpr现在称为eval

scala> import scala.reflect.runtime._ // requires scala-reflect.jar
                                      // in REPL it's implicitly added 
                                      // to the classpath
                                      // but in your programs
                                      // you need to do this on your own
import scala.reflect.runtime

scala> val cm = universe.runtimeMirror(getClass.getClassLoader)
cm @ 41d0fe80: reflect.runtime.universe.Mirror = JavaMirror with scala.tools.nsc.interpreter.IMain$TranslatingClassLoader...

scala> import scala.tools.reflect.ToolBox // requires scala-compiler.jar
                                          // in REPL it's implicitly added 
                                          // to the classpath
                                          // but in your programs
                                          // you need to do this on your own
import scala.tools.reflect.ToolBox

scala> val tb = cm.mkToolBox()
tb: scala.tools.reflect.ToolBox[reflect.runtime.universe.type] = scala.tools.reflect.ToolBoxFactory$ToolBoxImpl@3a962da5

scala> tb.runExpr(tb.parseExpr("class C; scala.reflect.classTag[C].runtimeClass"))
res2: Any = class __wrapper$1$f9d572ca0d884bca9333e251c64e980d$C$1

更新 #1. 如果你不需要一个java.lang.Class,只需要实例化编译后的类,那么你可以在提交给runExpr的字符串中直接写new C

更新 #2. 也可以使用自定义映射从变量名到运行时值来调用runExpr。例如:

scala> val build = scala.reflect.runtime.universe.build
build: reflect.runtime.universe.BuildApi = scala.reflect.internal.BuildUtils$BuildImpl@50d5afff

scala> val x = build.setTypeSignature(build.newFreeTerm("x", 2), typeOf[Int])
x: reflect.runtime.universe.FreeTermSymbol = free term x

scala> tb.runExpr(Apply(Select(Ident(x), newTermName("$plus")), List(Literal(Constant(2)))))
res0: Any = 4
在这个例子中,我创建了一个自由项,它的值为2(值不一定是原始值——也可以是您的自定义对象),并将标识符绑定到它上面。然后,在编译和运行工具箱执行的代码中直接使用该值。
示例使用手动AST组装,但是可以编写一个函数来解析字符串,找出未绑定的标识符,在某些映射中查找它们的值,并创建相应的自由项。Scala 2.10.0中没有这样的函数。

2
当然。使用 <mirror>.classSymbol(<instance of java.lang.Class>),其中 <mirror> = scala.reflect.runtime.universe.runtimeMirror(<instance of java.lang.Class>.getClassLoader)。然后您将获得一个Scala反射符号,可以使用Scala反射API进行检查。 - Eugene Burmako
2
reflect.runtime.currentMirror本质上是相同的,但我想拼写出完整版本。classTag[C].runtimeClass?没有特别的原因。我同意,classOf[C]更短。 - Eugene Burmako
2
@EugeneBurmako,找到未绑定的标识符的最佳方法是什么? - Sagie Davidovich
答案似乎已经过时了,适用于2.11版本。然而,由于该函数明确提到了2.10版本,我不确定是否值得更新?注意:与此同时,我已经在stackoverflow上提出了类似的问题,针对2.11版本。 - Suma
1
请参考http://docs.scala-lang.org/overviews/macros/changelog211.html,了解有关迁移到2.11 / 保持与2.10和2.11兼容性的指示。 - Eugene Burmako
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接