scala中重写属性与方法的行为

scala中重写属性与方法的行为,scala,Scala,让我们定义最简单的继承: class A { val base: String = { println("a hello"); "hello" } } class B extends A { override val base: String = { println("b hi"); "B hello"} } 现在让我们尝试一下: scala> new B().base a hello b hi res0: String = B hello 所以。

让我们定义最简单的继承:

class A           {          val base: String = { println("a hello"); "hello" } }
class B extends A { override val base: String = { println("b hi"); "B hello"} }
现在让我们尝试一下:

scala> new B().base
a hello
b hi
res0: String = B hello
所以。。你们中的许多人(或大多数人)可能不会对此感到惊讶我是。我(错误的…)假设
B.base
完全覆盖
A.base
。相反,我们看到两个方法都被调用:基本方法
A.base
然后是覆盖方法
B.base

那好吧。。那么,为了避免这种双重行为,
val
必须转换为
def
(方法),这是真的吗

现在我们有了想要的行为:

scala> new B().base
b hi
res1: String = B hello
scala> new B().base
b hi
res1: String = B hello
在这种情况下:在类的主体中重写
val
什么时候有用?

LAZY val

我突然想到

class A           {          lazy val base: String = { println("a hello"); "hello" } }
class B extends A { override lazy val base: String = { println("b hi"); "B hello"} }
现在我们得到了预期的行为:

scala> new B().base
b hi
res1: String = B hello
scala> new B().base
b hi
res1: String = B hello
这是我(不正确的…)的假设,即B.base将完全覆盖A.base。相反,我们看到两个方法都被调用:基方法A.base,然后是重写方法B.base

不,我们没有。
println
调用
A.base
B.base
都发生在构造函数中,而不是在访问
base
时。这正是(非
lazy
)成员
val
的意思:右侧在构造函数中执行,结果(在您的情况下,只是
“hello”
“B hello”
)存储到字段中。访问
base
仅返回该字段的值

而且超类构造函数总是在子类构造函数之前完全执行,没有办法重写它(您可以重写它调用的任何方法,但您需要小心,以免它们依赖于子类尚未初始化的状态)

在类的主体中重写val什么时候有用


当然,当您希望子类“
val
具有不同的值时。

除了我在调用提供所需行为的
lazy val
构造之前偶然观察到的信息之外,这个答案没有提供任何其他信息。在任何情况下,在调用构造函数期间访问
base
(在非
lazy
情况下)都是一种公平的解释。不,它没有被访问,只是被初始化。要访问它,请尝试在
val base
之后的
A
的构造函数中添加
println(base)
:您会看到它访问了未初始化的
B.base
,正是因为它完全覆盖了
A.base
。您问题中的误解是,
println
是方法
base
的一部分,但它不是。