FAQ

Why is my abstract or overridden val null?

Language

Example

The following example illustrates how classes in a subclass relation witness the initialization of two fields which are inherited from their top-most parent. The values are printed during the constructor of each class, that is, when an instance is initialized.

abstract class A {
  val x1: String
  val x2: String = "mom"

  println(s"A: $x1, $x2")
}
class B extends A {
  val x1: String = "hello"

  println(s"B: $x1, $x2")
}
class C extends B {
  override val x2: String = "dad"

  println(s"C: $x1, $x2")
}

In the Scala REPL we observe:

scala> new C
A: null, null
B: hello, null
C: hello, dad

Only when we get to the constructor of C are both x1 and x2 properly initialized. Therefore, constructors of A and B risk running into NullPointerExceptions, since fields are null-valued until set by a constructor.

Explanation

A “strict” or “eager” val is a val which is not a lazy val. Initialization of strict vals is done in the following order:

  1. Superclasses are fully initialized before subclasses.
  2. Within the body or “template” of a class, vals are initialized in declaration order, the order in which they are written in source.

When a val is overridden, it’s more precise to say that its accessor method (the “getter”) is overridden. So the access to x2 in class A invokes the overridden getter in class C. That getter reads the underlying field C.x2. This field is not yet initialized during the construction of A.

Mitigation

The -Wsafe-init compiler flag in Scala 3 enables a compile-time warning for accesses to uninitialized fields:

-- Warning: Test.scala:8:6 -----------------------------------------------------
8 |  val x1: String = "hello"
  |      ^
  |      Access non-initialized value x1. Calling trace:
  |      ├── class B extends A {        [ Test.scala:7 ]
  |      │   ^
  |      ├── abstract class A { [ Test.scala:1 ]
  |      │   ^
  |      └── println(s"A: $x1, $x2")    [ Test.scala:5 ]
  |                        ^^

In Scala 2, the -Xcheckinit flag adds runtime checks in the generated bytecode to identify accesses of uninitialized fields. That code throws an exception when an uninitialized field is referenced that would otherwise be used as a null value (or 0 or false in the case of primitive types). Note that these runtime checks only report code that is actually executed at runtime. Although these checks can be helpful to find accesses to uninitialized fields during development, it is never advisable to enable them in production code due to the performance cost.

Solutions

Approaches for avoiding null values include:

Use class / trait parameters

abstract class A(val x1: String, val x2: String = "mom") {
  println("A: " + x1 + ", " + x2)
}
class B(x1: String = "hello", x2: String = "mom") extends A(x1, x2) {
  println("B: " + x1 + ", " + x2)
}
class C(x2: String = "dad") extends B(x2 = x2) {
  println("C: " + x1 + ", " + x2)
}
// scala> new C
// A: hello, dad
// B: hello, dad
// C: hello, dad

Values passed as parameters to the superclass constructor are available in its body.

Scala 3 also supports trait parameters.

Note that overriding a val class parameter is deprecated / disallowed in Scala 3. Doing so in Scala 2 can lead to surprising behavior.

Use lazy vals

abstract class A {
  lazy val x1: String
  lazy val x2: String = "mom"

  println("A: " + x1 + ", " + x2)
}
class B extends A {
  lazy val x1: String = "hello"

  println("B: " + x1 + ", " + x2)
}
class C extends B {
  override lazy val x2: String = "dad"

  println("C: " + x1 + ", " + x2)
}
// scala> new C
// A: hello, dad
// B: hello, dad
// C: hello, dad

Note that abstract lazy vals are supported in Scala 3, but not in Scala 2. In Scala 2, you can define an abstract val or def instead.

An exception during initialization of a lazy val will cause the right-hand side to be re-evaluated on the next access; see SLS 5.2.

Note that using multiple lazy vals incurs a new risk: cycles among lazy vals can result in a stack overflow on first access. When lazy vals are annotated as thread-safe in Scala 3, they risk deadlock.

Use a nested object

For purposes of initialization, an object that is not top-level is the same as a lazy val.

There may be reasons to prefer a lazy val, for example to specify the type of an implicit value, or an object where it is a companion to a class. Otherwise, the most convenient syntax may be preferred.

As an example, uninitialized state in a subclass may be accessed during construction of a superclass:

class Adder {
  var sum = 0
  def add(x: Int): Unit = sum += x
  add(1) // in LogAdder, the `added` set is not initialized yet
}
class LogAdder extends Adder {
  private var added: Set[Int] = Set.empty
  override def add(x: Int): Unit = { added += x; super.add(x) }
}

In this case, the state can be initialized on demand by wrapping it in a local object:

class Adder {
  var sum = 0
  def add(x: Int): Unit = sum += x
  add(1)
}
class LogAdder extends Adder {
  private object state {
    var added: Set[Int] = Set.empty
  }
  import state._
  override def add(x: Int): Unit = { added += x; super.add(x) }
}

Early definitions: deprecated

Scala 2 supports early definitions, but they are deprecated in Scala 2.13 and unsupported in Scala 3. See the migration guide for more information.

Constant value definitions (specified in SLS 4.1 and available in Scala 2) and inlined definitions (in Scala 3) can work around initialization order issues because they can supply constant values without evaluating an instance that is not yet initialized.

Contributors to this page: