覆盖val的行为的基本原理

时间:2011-10-07 08:08:51

标签: scala override lazy-evaluation

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

According to Martin Odersky,至少在非惰性情况下,行为是“指定的”。我很好奇为什么行为是这样指定的,以及为什么它在val是懒惰的时候会有所不同。

2 个答案:

答案 0 :(得分:12)

在成员定义之外的类定义的模板(“body”)中的代码是构造函数中的代码。初始化子类的实例时,总是调用父类的构造函数(在这种情况下,您调用的是不带参数的构造函数,否则语法将为class B extends A(arg1, arg2) { ... })。有关更多详细信息,请参阅Scala Language Specification中的第5.1节。

这就是为什么在第一种情况下评估println("A")的原因; val定义是构造函数代码的一部分。

当您考虑第二个示例中施工时发生的情况时,您从未询问过x中定义的A的值;现在因为它是lazy val,所以在需要它之前不会计算它。

您可以将lazy val视为不带参数的方法,并在第一次调用它们时缓存它们的输出。你没有在这里调用那个方法,你只是定义了它。

答案 1 :(得分:1)

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

在执行构造函数期间,执行val s(和其他语句)的所有初始化。因此,val x = println("A")作为构建A的一部分运行,override val x = println("B")作为B构建的一部分运行。它们都会打印一些字符串并将x初始化为()(类型为Unit),因此覆盖只是一个红色的鲱鱼。 .x(来自(new B).x)无效。

在懒惰的情况下,您正在将x初始化为类似于不带参数的函数并返回Unit。在您阅读.x之前不会运行,并且,由于x被覆盖,因此在这种情况下只会打印“B”。