A_P
A_P

Reputation: 336

Understanding Scala lazy val inheritance rules

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.

Upvotes: 1

Views: 222

Answers (1)

Alin Gabriel Arhip
Alin Gabriel Arhip

Reputation: 2638

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.

Upvotes: 2

Related Questions