[英]A deeper explanation of why Lazy Vals work in scala constructors?
我了解Lazy val通常用于解决Scala中的初始化顺序问题,但对于这种解释我一直感到困扰。 如果“ Lazy Val”在其首次访问期间被初始化,并且其父构造方法在它可能存在之前就在使用它-究竟发生了什么? 在下面的示例中,当调用“ println(” A:“ + x1)”时-类B还不存在..但是该值可以正确打印。 在确切的时刻,我们看到“ A:您好”-这是在A的构造函数中发生的,还是以某种方式延迟到B完全存在之前? 从某种意义上说,将其标记为“ Lazy”是否违反直觉地提前提供了它?
谢谢
(参考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"
}
对象本身不存在,但是对象中的字段可以存在并可以计算。
发生的是,它从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();
}
}
}
“之前”关系仅指初始化程序运行的顺序。
在堆上分配对象时,只需分配它,然后调用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)
这就是为什么通常的建议是使用defs而不是vals,就此而言,使用traits而不是class(
以确保
使用traits时,您更有可能听说并遵循第一个规则)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.