1

我已经在 scala lang 论坛上发布了这个问题,但不幸的是我没有得到任何答案。第二次机会 ?

我尝试将一个解释器嵌入一个解释器并评估一个 scala 片段到这个解释器中。我想在解释器中绑定一个自定义类的实例。总而言之,这看起来像:

import scala.tools.nsc._
import scala.tools.nsc.interpreter._

class C {
  def sayHello(s:String) = "hello "+s
}

object Main extends App {

 val c= new C

 val s = new Settings
 s.usejavacp.value=true

 val i = new IMain(s)
 i.bind("myC",c)
 i.bind("world","the world")

 val script = "println(myC.sayHello(world))"
 i.eval(script)   

}

当我在 Eclipse (Kepler) 中运行此代码段时 - OpenJDK6/7 适用于两者 - BSD OS Scala-2.11.0-M4 - 路径中的 scala-compiler.jar 如果我尝试在 repl 中运行相同的代码,它工作正常或直接使用 scalac file.scala 然后 scala -cp 。主要我收到以下错误

    error: not found value myC
    javax.script.ScriptException: compile-time error
    at scala.tools.nsc.interpreter.IMain.compile(IMain.scala:575)
    at scala.tools.nsc.interpreter.IMain.eval(IMain.scala:997)
    at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)

我能够使其在另一个操作系统(Win7)下工作,但通过添加 s.bootclasspath="path/to/my/classes"

我怀疑一些类路径问题

后来我能够通过用java调用替换scala命令行调用来使其运行: java -cp $CLASSPATH Main使用包含scala库的CLASSPATH它可以工作

我查看了 scala 命令,就像它以不同的方式在 java 路径中附加了 scala 库。

有人有什么建议吗?

谢谢

根据以下评论添加:

Scalac 不输出任何错误事实上,如果我运行:

  java -cp .:$SCALA_PATH/lib/scala-library.jar:$SCALA_PATH/lib/scala-compiler.jar:$SCALA_PATH/lib/scala-reflect.jar Main

或者按照建议的 scala -nobootcp 它可以工作(感谢您的宝贵建议)否则,如果我让 scala 使用 bootcp,则开始的行是以下行并且它失败了

  java -Xbootclasspath/a:/usr/home/pcohen/Dev/Scala/scala-2.11.0-M4/lib/akka-actors.jar[...] -classpath "" [...]

当 scala jar 附加到引导类路径时,就好像我的绑定失败了。我无法清楚地理解为什么这种引导类路径差异会影响我的课程。

4

1 回答 1

3

我不知道这是否有资格作为建议,但您知道吗:

scala> class C { def sayHello(s: String) = s"hello, $s" }
defined class C

scala> $intp.bind("world","the world")
world: String = the world
res0: scala.tools.nsc.interpreter.IR.Result = Success

scala> val c = new C
c: C = C@19878659

scala> $intp interpret "c sayHello world"
res2: String = hello, the world
res1: scala.tools.nsc.interpreter.IR.Result = Success

通过单独编译,您的示例适用于scala -nobootcp my.Main.

你可以看到更多-Dscala.repl.debug=true。您会看到(每个人都检查结果值,对吗? )的Error返回是由以下原因引起的:bindprintln(i.bind("myC",c))

java.lang.ClassCastException: intpbind.C cannot be cast to intpbind.C

以及尝试它的包装器代码。

更多的话:

你说,“这就像我的绑定失败了”,但正如我在上面展示的那样,正是绑定失败了。

原因是当你说“绑定”时,你的真正意思是“自动生成一些代码,就像我写的一样,scala> val myC = c.”。哦,编译它,然后制作myC一个可以被 REPL 会话的其余部分导入的符号。

当您在引导类路径上使用 REPL 编译该代码时,引导类路径必须能够看到您的类C。(它也必须是你的应用程序类加载器加载的同一个类,加载你的主类的那个;这通常是由于委托而成立的。)

所以,这有效:

scalac -d /tmp/out mytest.scala
scala -J-Xbootclasspath/a:/tmp/out mytest.Test

您可以通过仅将一些类放在引导类路径上来进行细微差别,或者,更有可能的是,您将按照我的建议进行操作,并将 scala 从引导类路径中移除。

我没有对此进行研究,但是您可以使用嵌入在托管环境中的 REPL 搜索有类似问题的人,在这些环境中,您所在的类加载器非常重要。

奖励实用程序:

package scala {
  package object foo {
    def show(cl: ClassLoader): String = scala.reflect.runtime.ReflectionUtils.show(cl)
  }
}

使用 scala-private 实用程序来转储您感兴趣的类加载器:

  import scala.foo._
  Console println show(getClass.getClassLoader)
于 2013-08-29T06:10:29.433 回答