在Scala 2.11中运行时动态编译Scala类文件 [英] Dynamically compiling scala class files at runtime in Scala 2.11

查看:894
本文介绍了在Scala 2.11中运行时动态编译Scala类文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have the following code that works in Scala 2.10 to compile external classes at runtime in 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()
}

然后在其他地方,我可以使用classloader引用实例化类,例如

And then elsewhere, I could use the classloader reference to instantiate classes e.g.

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)

但是,由于从scala.tools.nsc.interpreter.IMain中删除了getInterpreterClassLoader方法,上述内容不再起作用.另外,AbstractFileClassLoader已被移动和弃用.不再允许从外部包中的类加载器中调用findClass方法.

However the above no longer works as getInterpreterClassLoader method has been removed from scala.tools.nsc.interpreter.IMain. Also, AbstractFileClassLoader has been moved and deprecated. It is no longer allowed to call findClass method in the class loader from an external package.

在Scala 2.11中推荐做上述事情的方法是什么?谢谢!

What is the recommended way to do the above in Scala 2.11? Thanks!

推荐答案

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

If your goal is to run external scala classes in runtime, I'd suggest using eval with scala.tools.reflect.ToolBox (it is included in REPL, but for normal usage you have to add 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编译文件.

You also can compile files, using tb.compile.

用示例修改:假设您的外部文件带有

Modified with example: assume you have external file with

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并根据需要使用它:

Additional possibilities are (almost) unlimited, you can get full tree AST and work with it as you want:

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

有关这些技巧,请参见官方文档:

See official documentation for these tricks:

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

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

这篇关于在Scala 2.11中运行时动态编译Scala类文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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