In this code, x
is null during construction:
abstract class A {
val x: String
println(s"A: x is $x")
}
class B extends A {
val x: String = "foo"
println(s"B: x is $x")
}
new B()
A: x is null
B: x is foo
It can be fixed by adding lazy
to the declaration of x
in B
:
abstract class A {
val x: String
println(s"A: x is $x")
}
class B extends A {
lazy val x: String = "foo"
println(s"B: x is $x")
}
new B()
A: x is foo
B: x is foo
I don't understand exactly why. It's counterintuitive that making a val lazy
should cause it to be initialized earlier than the corresponding non-lazy val would have been.
CodePudding user response:
An implementing val
definition in a subclass is evaluated - only after- its superclass has been initialized. Since you require the evaluation of the val
before the superclass has finished initialization, the JVM knowns it's not yet initialized so it gives you the default value for an uninitialized String
, which is null
.
This is one of the reasons why lazy val
s were built into the language:
When you prefix a val
definition with the lazy
modifier, the initializing expression on the right-hand side will only be evaluated the first time the val
is used. Since you require the val
to be evaluated in the superclass, it triggers it's evaluation. That is why it becomes available in the superclass.