以下是仅使用标准 Scala 的方法。不明显的东西都在GreenhouseFactory
:
package customizable
abstract class Plant
case class Rose() extends Plant
abstract class Greenhouse {
def getPlant(): Plant
}
case class GreenhouseFactory(implFilename: String) {
import reflect.runtime.currentMirror
import tools.reflect.ToolBox
val toolbox = currentMirror.mkToolBox()
import toolbox.u._
import io.Source
val fileContents = Source.fromFile(implFilename).getLines.mkString("\n")
val tree = toolbox.parse("import customizable._; " + fileContents)
val compiledCode = toolbox.compile(tree)
def make(): Greenhouse = compiledCode().asInstanceOf[Greenhouse]
}
object Main {
def main(args: Array[String]) {
val greenhouseFactory = GreenhouseFactory("external.scala")
val greenhouse = greenhouseFactory.make()
val p = greenhouse.getPlant()
println(p)
}
}
将您的覆盖表达式放在external.scala
:
new Greenhouse {
override def getPlant() = new Rose()
}
输出是:
Rose()
唯一棘手的事情是GreenhouseFactory
需要预先添加该import
语句以提供对外部文件所需的所有类型和符号的访问。为了简单起见,用所有这些东西制作一个单独的包。
编译器ToolBox
有点记录在这里。除了奇怪的导入之外,您真正需要知道的唯一一件事是toolbox.parse
将字符串(Scala 源代码)转换为抽象语法树,toolbox.compile
并将抽象语法树转换为带有签名的函数() => Any
。由于这是动态编译的代码,因此您必须忍受将其转换Any
为您期望的类型。