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然后将根据需要初始化值。

这种构造速度很快(所有应用程序运行时的值只初始化一次)并且是线程安全的。

于 2012-08-29T20:18:08.753 回答
8

据我了解,这与 Scala 类的创建方式有关。在 Java 中,上面定义的类将内联初始化变量,由于a尚未定义,因此无法编译。但是,在 Scala 中,它更相当于 Java 中的 this(在相同的场景中也应该产生 null):

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

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

或者,您可以声明b惰性,这将推迟设置值直到它被调用(此时将设置 a)。

于 2012-08-29T20:23:58.723 回答
6

如果这是一个问题,请在开发期间编译-Xcheckinit并迭代直到异常消失。

Spec 5.1 用于按顺序执行的模板主体语句;4.0 开始,用于块中的前向引用。

前向引用 - 为什么这段代码会编译?

于 2012-08-30T05:35:05.580 回答
3

正如@paradigmatic 所说,这并不是一个真正的错误。它是初始化顺序,它遵循声明顺序。在这种情况下,当被声明/初始化a时为空。b

将行更改private val b = new B(a)private lazy val b = new B(a)将解决问题,因为使用惰性会延迟初始化。b 的第一次使用。

这种行为很可能在 SLS 中有所描述。

于 2012-08-29T20:23:38.977 回答