2012-08-29 16 views
18

可能性の重複:
Scala: forward references - why does this code compile?スカラ座と前方参照

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)に設定され、コンストラクタ(つまりオブジェクト本体)が実行されます。

ちょうどあなたが前方(この場合の値a)を参照している宣言の前にlazyキーワードを追加し、それを動作させるには:

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は、オンデマンドで初期化されます。

この構成は高速です(値はすべてのアプリケーション実行時に1回しか初期化されず、スレッドセーフです)。

+11

'lazy'を追加すると、文を間違った順序で実行していることが分かっている場合に機能します。しかし、もしあなたが正しい順序でそれらを持っていると思うなら、おそらくあなたはその "不要な" "怠け者"を加えることを考えないでしょう。 :/ – kornfridge

2

@paradigmatic状態では、実際にはバグではありません。これは宣言の順序に続く初期化順序です。この場合、bが宣言された/ init-edされた場合、aはヌルです。

private val b = new B(a)からprivate lazy val b = new B(a)に変更すると、lazyを使用するとinitが遅れるため、問題が修正されます。 bの最初の使用法です。

この動作はSLSで説明されている可能性があります。

7

これは私が理解している方法ですが、これはScalaクラスの作成方法と関係があります。 Javaでは、上で定義したクラスは変数をインラインで初期化することになり、aはまだ定義されていないため、コンパイルできませんでした。しかし、Scalaで、それは(も同じシナリオにヌル生成しなければならない)Javaでこれより同等です:

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

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

代わりに、あなたはまで値を設定延期することになる、bのあなたの宣言は怠惰作ることができますそれが呼び出されます(その時点でaが設定されます)。

6

これが問題になる場合は、開発中に-Xcheckinitをコンパイルし、例外がなくなるまで繰り返します。

仕様5.1テンプレート本体のステートメントを順番に実行します。ブロック内の前方参照のための4.0の開始。

Forward References - why does this code compile?

関連する問題