繁体   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