我理解Lazy vals的一般用法来解决scala中的初始化顺序问题,但是有些事总是困扰我这个解释。如果在第一次访问期间初始化了“Lazy Val”,并且父构造函数在它可能存在之前正在使用它 - 这究竟是什么?在下面的例子中,当调用“println(”A:“+ x1)”时 - B类尚不存在..但值正确打印。在我们看到“A:Hello”的确切时刻 - 这是在A的构造函数中发生的,还是以某种方式延迟直到B完全存在?从某种意义上说,将它标记为“懒惰”已经反直觉地使其提前提供了吗?
谢谢
(引自https://github.com/paulp/scala-faq/wiki/Initialization-Order)
abstract class A {
val x1: String
println("A: " + x1)
}
class B extends A {
lazy val x1: String = "hello"
}
答案 0 :(得分:2)
对象本身不存在,但对象中的字段可以存在并被计算。
正在发生的事情是,在A的构造函数中,它正在访问x1,因此强制计算延迟值。 A可以知道它需要调用B的x1方法的原因是因为它是动态调度的(就像在Java中一样)。
如果有帮助,那么堆栈将类似于:
B.x1$lzycompute
B.x1
A.<init>
B.<init>
如果有帮助,这里是Java代码的粗略版本:
public class Testing {
public static void main(String[] args) {
new B();
}
public static abstract class A {
public abstract String x1();
public A() {
System.out.println(x1());
}
}
public static class B extends A {
private boolean inited = false;
private String x1;
private String computeX1() {
x1 = "hello";
inited = true;
return x1;
}
public String x1() {
return this.inited ? x1 : computeX1();
}
}
}
答案 1 :(得分:2)
“BEFORE”关系只是指运行初始值设定项的顺序。
在堆上分配对象时,只需分配它,然后调用init方法来初始化它。
在孩子B的实例之前有一个父A的实例是没有意义的。
它们是同一个对象,被视为其类型的部分。
当人们告诉我我看起来像我的父亲(不是我)时,情况并非如此。
无论如何,如果它不是一直懒惰的话,就会出现脆弱:
abstract class A {
val x1: String
val x2: String
println("A: " + x1)
println("A2: " + x2)
}
class B extends A {
lazy val x1: String = "hello"
lazy val x2: String = x3
val x3: String = "bye"
}
object Test extends App {
val b = new B
Console println (b.x1,b.x2,b.x3)
}
结果:
A: hello
A2: null
(hello,null,bye)
这就是为什么一般的建议是使用def而不是vals,并且就此而言,使用traits而不是类(以确保,因为有了特征你更有可能听说过并遵循第一个规则)。