27

重複の可能性:
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)
  }

}

次のコードは「null」を出力します。ジャバで。同様の構造は、無効な前方参照のためにコンパイルされません。問題は、なぜ Scala でうまくコンパイルできるのかということです。それは設計によるものですか、SLS で説明されていますか、それとも単に 2.9.1 のバグですか?

4

4 に答える 4

38

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

この構築は高速で (値はすべてのアプリケーション ランタイムに対して 1 回だけ初期化されます)、スレッドセーフです。

于 2012-08-29T20:18:08.753 に答える
8

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

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

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

別の方法として、lazy の宣言を行うこともできます。bこれにより、呼び出されるまで値の設定が延期されます (その時点で a が設定されます)。

于 2012-08-29T20:23:58.723 に答える
6

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

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

前方参照 - このコードがコンパイルされるのはなぜですか?

于 2012-08-30T05:35:05.580 に答える
3

@paradigmatic が述べているように、これは実際にはバグではありません。宣言順に従うのが初期化順です。この場合、宣言/初期化さaれると null になります。b

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

この動作は SLS に記述されている可能性が非常に高いです。

于 2012-08-29T20:23:38.977 に答える