3

我了解 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"    

}
4

2 回答 2

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();
        }
    }

}
于 2013-08-12T03:45:37.243 回答
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)

这就是为什么一般建议是使用 defs 而不是 vals,并且就此而言,使用特征而不是类(以确保由于使用特征,您更有可能听说过并遵循第一条规则)。

于 2013-08-12T03:56:48.020 回答