Here There Be Dragons - Dangers of Initialization Order in Scala

One “best practice” in Scala is to use def for abstract values in a trait, never val. To wit:

trait Foo {
  // Legal, but wrong
  val myAbstractString: String

  // Best practice
  def myOtherAbstractString: String

}

The reason def is preferred is to avoid initialization order weirdness demonstrated below. Basically, the value will first be initialized to its default (null for references, 0 for integers), and then updated to its correct value.

trait A {
  val i: String // danger zone
  def j: String
}

class B extends A {
  println ("val i = " + i) // oops, i is null
  println ("def j = " + j) // prints "j", as expected

  val i = "i" // only now is i's value equal to "i"
  def j = "j"
}

val b = new B

However, you cannot truly defend against initialization order problems just by using an abstract def instead of a val. This is because an abstract def can be implemented by a val, and this exposes the same problem.

trait C {
  def j: String
}

class D extends C {
  println ("j = " + j) // prints null

  val j = "j" // uh oh. use a def, or a lazy val
}

val d = new D

What’s the solution?

You must use either a def or a lazy val in the child class, in addition to the abstract defin the parent trait.

It’s worth pointing out that even though using lazy val will often fix this problem, it is not foolproof. If a non-lazy val uses the lazy val in its initialization, the same risk exists because you’ve rendered the lazy modifier useless.

Bonus!

You can actually produce this same error in a plain vanilla class. Terrible, right?

class Wat {
  println("s: " + s) // prints null
  val s = "hmm"
}

This same sort of error is protected against by the compiler if you try doing that exact same shady business in a function:

// Won't compile! Thank you, compiler.
// error: "forward reference extends over definition of value s"
class Wat {
  def foo() {
    println("s: " + s)
    val s = "hmm"
  }
}

That’s all for now.