如何重新加载Scala REPL中的类或包?

我几乎总是有一个或两个开放的Scala REPL会话,这使得对Java或Scala类进行快速testing变得非常容易。 但是如果我改变一个类并重新编译它,那么REPL会继续加载旧的。 有没有办法让它重新加载类,而不是必须重新启动REPL?

只是举一个具体的例子,假设我们有Test.scala文件:

object Test { def hello = "Hello World" } 

我们编译它并启动REPL:

 ~/pkg/scala-2.8.0.Beta1-prerelease$ bin/scala Welcome to Scala version 2.8.0.Beta1-prerelease (Java HotSpot(TM) Server VM, Java 1.6.0_16). Type in expressions to have them evaluated. Type :help for more information. scala> Test.hello res0: java.lang.String = Hello World 

然后我们改变源文件

 object Test { def hello = "Hello World" def goodbye = "Goodbye, Cruel World" } 

但我们不能使用它:

 scala> Test.goodbye <console>:5: error: value goodbye is not a member of object Test Test.goodbye ^ scala> import Test; <console>:1: error: '.' expected but ';' found. import Test; 

类重装不是一个简单的问题。 事实上,这是JVM变得非常困难的事情。 你有几个选项,但:

  • 在debugging模式下启动Scala REPL。 JVMdebugging器有一些内置的重新加载,在方法级别上工作。 它不会帮助你的情况,但它会处理一些简单的事情,比如改变一个方法的实现。
  • 使用JRebel( http://www.zeroturnaround.com/jrebel )。 JRebel基本上是JVM的一个超级负载类重载解决scheme。 它可以处理成员的添加/删除,新的/删除的类,定义的变化等。它只能处理类层次结构中的变化(例如添加超级接口)。 这不是一个免费的工具,但它们提供的补充许可仅限于Scala编译单元。

不幸的是,这两个都受到Scala REPL的实现细节的限制。 我使用JRebel,它通常会这样做,但是仍然会有REPL不能反映重载类的情况。 不过,总比没有好。

如果目标是不必重复先前的命令,则可以select重新加载class级。 REPL有命令

 :replay 

它会重新启动REPL环境并播放所有以前的有效命令。 (无效的跳过了,所以如果之前是错的,它不会突然起作用)。当REPL被重置,它重新加载类,所以新的命令可以使用重新编译的类的内容(实际上,旧的命令也将使用这些重新编译的类)。

这不是一个通用的解决scheme,但它是一个有用的快捷方式,可以用可重新计算的状态来扩展单个会话。

注意:这适用于裸露的Scala REPL。 如果从SBT或其他环境运行它,它可能会也可能不会运行,这取决于SBT或其他环境如何打包类 – 如果不更新正在使用的实际类path上的内容,当然会赢得“工作!

有一个命令满足你的要求

 :load path/to/file.scala 

这将重新加载scala源文件并重新编译到类,然后你可以重播你的代码

这对我有用….

如果你的新源文件Test.scala看起来像这样…

 package com.tests object Test { def hello = "Hello World" def goodbye = "Goodbye, Cruel World" } 

您首先必须将新更改加载到Scala控制台(REPL)中。

 :load src/main/scala/com/tests/examples/Test.scala 

然后重新导入包,以便在Scala控制台中引用新代码。

 import com.tests.Test 

现在享受你的新代码,无需重新开始你的会议:)

 scala> Test.goodbye res0: String = Goodbye, Cruel World 

如果.scala文件位于启动REPL的目录中,则可以省略完整path,只需放入:load myfile.scala ,然后导入。