从字符串生成一个类并在 Scala 2.10 中实例化它 [英] Generating a class from string and instantiating it in Scala 2.10

查看:25
本文介绍了从字符串生成一个类并在 Scala 2.10 中实例化它的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

解决方案

W.r.t 编译工具箱只能运行表达式 = 返回值,但不能运行带有编译结果的类或文件/字节数组.

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

编辑.在 2.10.0-RC1 中,ToolBox 的一些方法已被重命名.parseExpr 现在只是 parserunExpr 现在被称为 eval.

scala>import scala.reflect.runtime._//需要 scala-reflect.jar//在 REPL 中它是隐式添加的//到类路径//但在你的程序中//你需要自己做这件事导入 scala.reflect.runtime标度>val cm = Universe.runtimeMirror(getClass.getClassLoader)cm @ 41d0fe80:reflect.runtime.universe.Mirror = JavaMirror with scala.tools.nsc.interpreter.IMain$TranslatingClassLoader...标度>import scala.tools.reflect.ToolBox//需要 scala-compiler.jar//在 REPL 中它是隐式添加的//到类路径//但在你的程序中//你需要自己做这件事导入 scala.tools.reflect.ToolBox标度>val tb = cm.mkToolBox()tb: scala.tools.reflect.ToolBox[reflect.runtime.universe.type] = scala.tools.reflect.ToolBoxFactory$ToolBoxImpl@3a962da5标度>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构建:reflect.runtime.universe.BuildApi = scala.reflect.internal.BuildUtils$BuildImpl@50d5afff标度>val x = build.setTypeSignature(build.newFreeTerm("x", 2), typeOf[Int])x:reflect.runtime.universe.FreeTermSymbol = 免费项 x标度>tb.runExpr(Apply(Select(Ident(x), newTermName("$plus")), List(Literal(Constant(2)))))res0:任意 = 4

在本例中,我创建了一个值为 2 的自由项(该值不必是原始值 - 它可以是您的自定义对象)并为其绑定一个标识符.然后在由工具箱编译和运行的代码中按原样使用该值.

该示例使用手动 AST 汇编,但可以编写一个函数来解析字符串,找出未绑定的标识符,在某些映射中查找它们的值,然后创建相应的自由项.Scala 2.10.0 中没有这样的功能.

In Scala 2.10 how do I generate a class from string (probably, using the Toolbox api) later to be instantiated with Scala's reflection?

解决方案

W.r.t compilation toolboxes can only run expressions = return values, but not resulting classes or files/byte arrays with compilation results.

However it's still possible to achieve what you want, since in Scala it's so easy to go from type level to value level using implicit values:

Edit. In 2.10.0-RC1 some methods of ToolBox have been renamed. parseExpr is now just parse, and runExpr is now called 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

Update #1. If you don't need a java.lang.Class and just need to instantiate the compiled class, you can write new C directly in the string submitted to runExpr.

Update #2. It is also possible to have runExpr use custom mapping from variable names to runtime values. For example:

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

In this example I create a free term that has a value of 2 (the value doesn't have to be a primitive - it can be your custom object) and bind an identifier to it. This value is then used as-is in the code that is compiled and run by a toolbox.

The example uses manual AST assembly, but it's possible to write a function that parses a string, finds out unbound identifiers, looks up values for them in some mapping and then creates corresponding free terms. There's no such function in Scala 2.10.0 though.

这篇关于从字符串生成一个类并在 Scala 2.10 中实例化它的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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