簡體   English   中英

更深入的解釋為何Lazy Vals在Scala構造函數中起作用?

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM