覆写val的行为的理由

6
class A { 
  val x = println("A") 
}
class B extends A {
  override val x = println("B")
}
(new B).x

输出:

A
B

然而,
class A { 
  lazy val x = println("A") 
}
class B extends A {
  override lazy val x = println("B")
}
(new B).x

仅输出:

B

根据Martin Odersky的说法,至少在非惰性情况下,行为是“按规定的方式”。我很好奇为什么会有这样的规定,并且为什么在val是惰性时会有所不同。

2个回答

12
在类定义的模板中(“body”)的代码,即成员定义之外的部分,就是构造函数的内容。当您初始化子类的实例时,父类的构造函数总是会被调用(在本例中,您调用一个不带参数的构造函数,否则语法将为 class B extends A(arg1, arg2) { ... })。有关详细信息,请参见 Scala 语言规范第 5.1 节。
这就是为什么在第一种情况下会评估println("A")的原因;该val定义是构造函数代码的一部分。
当您考虑第二个示例中的构建时间发生了什么时,您从未请求定义在A中的x的值;现在,因为它是一个lazy val,它将在需要时才被计算。
您可以将lazy val视为不带参数且缓存其输出的方法。您没有在此处调用该方法,而是只定义了它。

1
还要注意:(1) class A { lazy val x: Int = throw new IllegalStateException("AAA"); val z = x; println(z) } (2) class B extends A { override lazy val x = 5 } (3) (new B) 打印出 5,没有抛出异常,因为延迟值访问实际上是覆盖方法的方法调用。 - ron

1
class A { 
  val x = println("A") 
}
class B extends A {
  override val x = println("B")
}
(new B).x

在构造函数执行期间,所有val的初始化(和其他语句)都会被执行。因此,val x = println("A") 作为A的构造的一部分运行,override val x = println("B") 作为B的构造的一部分运行。它们都打印一些字符串并将x初始化为()(类型为Unit),因此在这里覆盖只是一个红鱼。从(new B).x中的.x什么也不做。
在懒加载的情况下,您将x初始化为类似于不带参数并返回Unit的函数。直到读取.x时才运行它,并且由于x被覆盖,因此在这种情况下只会打印“B”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接