【问题标题】:Dynamically compiling scala class files at runtime in Scala 2.11在 Scala 2.11 运行时动态编译 scala 类文件
【发布时间】:2016-08-25 05:16:14
【问题描述】:

我有以下代码可在 Scala 2.10 中运行,以便在 Scala 中运行时编译外部类

/**
  * Compile scala files and keep them loaded in memory
  * @param classDir Directory storing the generated scala files
  * @throws IOException if there is problem reading the source files
  * @return Classloader that contains the compiled external classes
  */
@throws[IOException]
def compileFiles(classDir: String): AbstractFileClassLoader = {
  val files = recursiveListFiles(new File(classDir))
                  .filter(_.getName.endsWith("scala"))
  println("Loaded files: \n" + files.mkString("[", ",\n", "]"))

  val settings: GenericRunnerSettings = new GenericRunnerSettings(err => println("Interpretor error: " + err))
  settings.usejavacp.value = true
  val interpreter: IMain = new IMain(settings)
  files.foreach(f => {
    interpreter.compileSources(new BatchSourceFile(AbstractFile.getFile(f)))
  })

  interpreter.getInterpreterClassLoader()
}

然后在其他地方,我可以使用类加载器引用来实例化类,例如

val personClass = classLoader.findClass("com.example.dynacsv.PersonData")
val ctor = personClass.getDeclaredConstructors()(0)
val instance = ctor.newInstance("Mr", "John", "Doe", 25: java.lang.Integer, 165: java.lang.Integer, 1: java.lang.Integer)
println("Instantiated class: " + instance.getClass.getCanonicalName)
println(instance.toString)

然而,上述方法不再有效,因为 getInterpreterClassLoader 方法已从 scala.tools.nsc.interpreter.IMain 中删除。此外,AbstractFileClassLoader 已被移动和弃用。不再允许从外部包调用类加载器中的findClass方法。

在 Scala 2.11 中执行上述操作的推荐方法是什么?谢谢!

【问题讨论】:

    标签: scala scala-2.11


    【解决方案1】:

    如果您的目标是在运行时运行外部 scala 类,我建议将 eval 与 scala.tools.reflect.ToolBox 一起使用(它包含在 REPL 中,但对于正常使用,您必须添加 scala-reflect.jar):

    import scala.reflect.runtime.universe
    import scala.tools.reflect.ToolBox
    val tb = universe.runtimeMirror(getClass.getClassLoader).mkToolBox()
    tb.eval(tb.parse("""println("hello!")"""))
    

    你也可以编译文件,使用tb.compile

    用示例修改:假设您有带有

    的外部文件
    class PersonData() {
      val field = 42
    }
    scala.reflect.classTag[PersonData].runtimeClass
    

    所以你这样做

    val clazz = tb.compile(tb.parse(src))().asInstanceOf[Class[_]]
    val ctor = clazz.getDeclaredConstructors()(0)
    val instance = ctor.newInstance()
    

    其他可能性(几乎)是无限的,您可以获得完整的树 AST 并根据需要使用它:

    showRaw(tb.parse(src)) // this is AST of external file sources
    // this is quasiquote
    val q"""
          class $name {
            ..$stats
          }
          scala.reflect.classTag[PersonData].runtimeClass
        """ = tb.parse(src)
    // name: reflect.runtime.universe.TypeName = PersonData
    // stats: List[reflect.runtime.universe.Tree] = List(val field = 42)
    println(name) // PersonData
    

    请参阅官方文档了解这些技巧:

    http://docs.scala-lang.org/overviews/reflection/symbols-trees-types.html

    http://docs.scala-lang.org/overviews/quasiquotes/intro.html

    【讨论】:

    • 编译后如何取回未知类的实例? tb.compile(tb.parse(source)).asInstanceOf[PersonData] 不起作用,因为 PersonData 在正在编译的源代码中。 linked question 中添加了一个工作示例,您能否修改它以使用 ToolBox?谢谢
    • 您可以修改源代码以在最终语句中引用已定义的类吗?如果是,那么您只需要使用asInstanceOf[Class[_]] 获取该类,然后根据需要使用它。
    • 我的意思是在源文件中有类似case class PersonData(i: Int); scala.reflect.classTag[PersonData].runtimeClass
    • 用一个例子修改了我的答案。请注意,您必须继续使用反射来访问字段。
    • 谢谢,是的,我可以修改源文件并添加 classTag 行。这样现在就可以了。一个问题:instance.getClass.getName 给出类似:__wrapper$1$3ec23e8228d44c638bba98e65ef2c748.__wrapper$1$3ec23e8228d44c638bba98e65ef2c748$PersonData$3。有没有办法让PersonData 作为类名?此外,您的代码中val clazz 有错字(错过了申请),请修复它,以便对其他人有所帮助。应该是val clazz = tb.compile(tb.parse(src))().asInstanceOf[Class[_]]
    猜你喜欢
    • 2019-11-17
    • 1970-01-01
    • 1970-01-01
    • 2015-10-03
    • 1970-01-01
    • 2014-11-13
    • 2014-11-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多