覆盖 val 的行为的基本原理
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 是惰性的时它会有所不同。
class A {
val x = println("A")
}
class B extends A {
override val x = println("B")
}
(new B).x
Prints:
A
B
However,
class A {
lazy val x = println("A")
}
class B extends A {
override lazy val x = println("B")
}
(new B).x
Prints just:
B
According to Martin Odersky, the behaviour, at least in the non-lazy case, is "as specified". I'm curious as to why the behaviour is specified that way, and why it differs when the val is lazy.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
类定义的模板(“主体”)中的代码(在成员定义之外)是进入构造函数的内容。当您初始化子类的实例时,始终会调用父类的构造函数(在本例中,您调用的是不带参数的构造函数,否则语法将为
class B extends A(arg1, arg2) { .. }
)。有关更多详细信息,请参阅 Scala 语言规范 中的第 5.1 节。这就是为什么
println("A")
在第一种情况下被评估;val
定义是构造函数代码的一部分。当您考虑第二个示例中构造时发生的情况时,您从未询问过
A
中定义的x
的值;现在因为它是一个lazy val
,所以在需要它之前不会被计算。您可以将惰性 val 视为不带参数的方法,并在第一次调用时缓存其输出。您没有在这里调用该方法,您只是定义了它。
The code in the template ("body") of a class definition, outside of member definitions, is what goes into the constructor. Constructors for parent classes are always called when you initialize an instance of a child class (in this case, you are calling a constructor with no arguments, otherwise the syntax would be
class B extends A(arg1, arg2) { ... }
). For more details, see Section 5.1 in the Scala Language Specification.That is why
println("A")
is evaluated in the first case; thatval
definition is part of the constructor code.When you think about what happens at construction time in the second example, you never asked for the value of
x
defined inA
; now because it is alazy val
, it will not be computed before it is needed.You can think of
lazy val
s as methods that take no argument and that cache their output the first time they are called. You didn't call that method here, you just defined it.在构造函数的执行过程中,会执行
val
(以及其他语句)的所有初始化。因此,val x = println("A")
作为A
构造的一部分运行,并覆盖 val x = println("B")
code> 作为B
构造的一部分运行。它们都打印一些字符串并将x
初始化为()
(类型为Unit
),因此覆盖在这里只是转移注意力。.x
(来自(new B).x
)不执行任何操作。在惰性情况下,您将
x
初始化为类似不带参数并返回Unit
的函数。在您读取.x
之前,它不会运行,并且由于x
被覆盖,因此在这种情况下仅打印“B”。During the execution of the constructor, all the initializations of
val
s (and other statements) are executed. Therefore,val x = println("A")
is run as part of the construction ofA
andoverride val x = println("B")
is run as part of the construction ofB
. They both print some string and initializex
to()
(of typeUnit
), so override is just a red herring here. The.x
(from(new B).x
) does nothing.In the lazy case, you're initializing
x
to something like a function taking no arguments and returningUnit
. It is not run till you read.x
, and, sincex
is overridden, only "B" gets printed in that case.