可能重复:
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)
}
}
以下代码打印“null”。在java中。由于前向引用无效,类似的构造无法编译。问题是 - 为什么它在Scala中编译得很好?这是设计,在SLS中描述还是仅仅是2.9.1中的错误?
答案 0 :(得分:27)
这不是一个bug,而是学习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 :(得分:7)
我理解它的方式,这与Scala类的创建方式有关。在Java中,上面定义的类将内联初始化变量,并且由于a
尚未定义,因此无法编译。但是,在Scala中,它在Java中更相当于它(在同一场景中也应该生成null):
class Omg {
private B b = null;
private A a = null;
Omg(){
b = new B(a);
a = new A();
}
}
或者,您可以声明b
lazy,这将推迟设置值,直到调用它为止(此时将设置a)。
答案 2 :(得分:6)
如果这是一个问题,请在开发过程中使用-Xcheckinit
进行编译并迭代,直到异常消失为止。
规范5.1按顺序执行的模板体语句; 4.0的开头,用于块中的前向引用。
答案 3 :(得分:2)
正如@paradigmatic所说,这不是一个真正的错误。它是初始化顺序,遵循声明顺序。在这种情况下,a
在声明/初始化b
时为空。
将行private val b = new B(a)
更改为private lazy val b = new B(a)
将解决问题,因为使用lazy会延迟init。 b对它的第一次使用。
很可能在SLS中描述了这种行为。