2010-03-18 64 views
39

我幾乎總是有一個或兩個Scala REPL會話,這使得Java或Scala類很容易進行快速測試。但是如果我改變一個類並重新編譯它,那麼REPL會繼續加載舊的。有沒有辦法讓它重新加載類,而不必重新啓動REPL?如何在Scala 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; 
+0

我想這是REPL工作表的一個好處 – nafg 2015-11-01 04:21:49

回答

26

類重裝不是一個簡單的問題。實際上,這是JVM變得非常困難的原因。儘管你有幾個選項:

  • 以調試模式啓動Scala REPL。 JVM調試器有一些內置的重新加載,可以在方法級別上工作。它不會幫助你處理你的情況,但它會處理一些簡單的事情,比如改變方法實現。
  • 使用JRebel的(http://www.zeroturnaround.com/jrebel)。 JRebel基本上是一款針對 JVM的超級充電類重裝解決方案。它可以處理差不多是唯一 成員追加/去除,新/刪除類,定義的變化,等它不能手柄處於類層次結構的變化(增加一個超級接口, 例子)。這不是一個免費的工具,但它們提供的補充許可僅限於Scala編譯單元。

不幸的是,這兩個由斯卡拉REPL的實現細節的限制。我使用JRebel,它通常會這樣做,但仍然有些情況下REPL不會反映重新加載的類(es)。儘管如此,它總比沒有好。

+7

如何在調試模式下啓動scala repl?我使用sbt並運行'console'爲我的項目啓動scala repl,但不知道如何在調試模式下完成此操作。 – mmrobins 2012-09-23 07:12:33

36

還有就是重新加載類,如果我們的目標是不用重複以前的命令替代。 REPL的命令爲

:replay 

它重新啓動REPL環境並播放所有以前的有效命令。 (無效的跳過了,所以如果之前它錯了,它不會突然起作用)。當REPL被重置時,它會重新加載類,所以新的命令可以使用重新編譯的類的內容(實際上,舊命令也將使用這些重新編譯的類)。

這不是一個通用的解決方案,但是有用的快捷方式與重新可計算狀態延伸的單個會話。

注:適用於裸斯卡拉REPL。如果您從SBT或其他環境運行它,它可能會也可能不會運行,這取決於SBT或其他環境如何打包類 - 如果您沒有更新正在使用的實際類路徑上的內容,當然它會贏得「工作!

+0

顯然對密集會議不利,但我喜歡這樣。這也意味着如果測試/ TDD是您的目標,您會知道您的更改是否破壞了任何內容。 – Grogs 2012-01-06 05:24:26

+4

我相信這是錯誤的。 ':replay' does * not * reload classes。 – 2014-07-23 06:45:30

+0

我同意@AdamMackler,至少它似乎沒有與2.10.4重新加載。 – mitchus 2015-06-02 20:20:24

5

有一個命令遇見你要求

:load path/to/file.scala 

這將刷新斯卡拉源文件並重新編譯的類,則可以重放代碼

+1

如果'file.scala'在頂部定義了一個'package',這對你有用嗎?謝謝 – 2016-05-16 23:21:18

5

這對我的作品....

如果你的新源文件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 
+0

只有在'Test.scala'頂部沒有定義'package'的情況下,這才適用於我。有任何想法嗎?謝謝 – 2016-05-16 23:19:10

+0

不知道。也許糟​​糕的包路徑 – cevaris 2016-05-17 14:05:03

0

如果.scala文件是在啓動REPL,你可以ommit的完整路徑的目錄,只是把:load myfile.scala,然後導入。

相關問題