让我们定义最简单的继承:
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
(方法),这是真的吗?
class A { def base: String = { println("a hello"); "hello" } }
class B extends A { override def base: String = { println("b hi"); "B hello"} }
现在我们有了理想的行为:
scala> new B().base
b hi
res1: String = B hello
鉴于这种情况:什么时候覆盖val
在一个类的主体中有用?
答案 0 :(得分:1)
我的(错误的......)假设B.base将完全覆盖A.base。相反,我们看到两个方法都被调用:基本方法A.base然后覆盖方法B.base。
不,我们没有。 println
和A.base
中的B.base
次调用在构造函数中发生,而不是在您访问base
时。这就是(非lazy
)成员val
的含义:右侧是在构造函数和结果中执行的(在您的案例中只是"hello"
或"B hello"
)存储在一个字段中。访问base
只会返回该字段的值。
超类构造函数总是在子类构造函数之前完全执行,没有办法覆盖它(你可以覆盖它调用的任何方法,但是你需要小心所以它们不依赖于子类'尚未初始化的状态'。
scala> val b = new B()
a hello
b hi
scala> b.base
res0: String = B hello
什么时候覆盖val在类的主体中有用?
当您希望子类“val
具有不同的值时”。
答案 1 :(得分:0)
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