Home > Software design >  Understanding Scala lazy val inheritance rules
Understanding Scala lazy val inheritance rules

Time:08-28

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 vals 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.

  • Related