2012-08-29 32 views
18

可能重複:
Scala: forward references - why does this code compile?Scala和向前引用

object Omg { 

    class A 

    class B(val a: A) 

    private val b = new B(a) 

    private val a = new A 

    def main(args: Array[String]) { 
    println(b.a) 
    } 

} 

下面的代碼打印 「空」。在java中。由於無效的前向參考,類似的結構不能編譯。問題是 - 爲什麼它在Scala中編譯得很好?這是由SLS描述的設計,還是2.9.1中的錯誤?

+1

令我困擾的問題是它允許val更改其值。這讓我很難過:-( – thoredge

+0

這有點奇怪 - 可能會導致很多錯誤,並且我依賴於Java行爲,這需要在使用它們之前初始化值。 – jdevelop

+1

@jdevelop即使java不能捕獲所有可能的前向引用 –

回答

23

這不是一個錯誤,而是學習Scala時的一個經典錯誤。當對象Omg被初始化時,首先將所有值設置爲默認值(在這種情況下爲null),然後運行構造函數(即對象體)。

要使其工作,只需添加lazy關鍵字在聲明你向前引用前面(價值a在這種情況下):

object Omg { 

    class A 

    class B(val a: A) 

    private val b = new B(a) 

    private lazy val a = new A 

    def main(args: Array[String]) { 
    println(b.a) 
    } 
} 

價值a將被按需初始化。

該構造速度很快(對於所有應用程序運行時,這些值只能初始化一次)並且線程安全。

+11

如果您知道*您正在以錯誤的順序執行語句,請添加'lazy'作品。但是如果你認爲你有正確的順序,那麼你可能不會想到添加這個「不必要」的「懶惰」。 :/ – kornfridge

2

作爲@paradigmatic狀態,它不是一個真正的錯誤。這是初始化順序,它遵循聲明順序。當b被聲明/初始化時,這種情況下,a爲空。

將行private val b = new B(a)更改爲private lazy val b = new B(a)會解決問題,因爲使用lazy會延遲init。的b到它的第一次使用。

這種行爲很可能在SLS中描述。

7

我理解它的方式,這與Scala類的創建方式有關。在Java中,上面定義的類將內聯初始化變量,並且由於尚未定義a,因此無法編譯。然而,在斯卡拉這更多的是這在Java中當量(其中也應在相同的情況下產生空):

class Omg { 
    private B b = null; 
    private A a = null; 

    Omg(){ 
    b = new B(a); 
    a = new A(); 
    } 
} 

或者,你可以讓你的b懶聲明,這將推遲設定值,直到它被稱爲(在哪個時間a將被設置)。

6

如果這是一個問題,請在開發過程中使用-Xcheckinit進行編譯並迭代,直到異常消失。

Spec 5.1用於按順序執行的模板主體語句;以塊爲單位的前向引用4.0開頭。

Forward References - why does this code compile?