I tried using an abstract val
in a trait to initialize another value. I got a NullPointerException
. I boiled the behaviour down to a minimal test case:
trait MessagePrinter {
val message: String
println(message)
}
class HelloPrinter extends MessagePrinter {
val message = "Hello World"
}
val obj = new HelloPrinter()
println(obj.message)
This little program yields the following result:
null
Hello World
I was under the impression that a val may never change. Is this expected behaviour or is it a compiler bug? How can I work around this issue and print Hello World
during initialization?
By section 5.1 of the Scala specification, super classes are initialized first. Even though
vals
cannot normally be reinstantiated, they do start with a default initial value during construction. You can either usedef
, which has different semantics:Or you might consider switching things around like so:
In which case the super classes are evaluated in order, so that the
MessagePrinter
initialization should work as desired.You should use
def
in both cases.One of the sources describing this behaviour is "Scala Puzzlers" Puzzler 4: