[英]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.