3

In the Programming in Scala (Ch. 10 on "Composition & Inheritance") book there is an example which causes some misunderstandings. This is the extracted part:

abstract class Element {
  def contents: Array[String]
  val someProperty: String = {
    println("=== Element")
    contents(0)
  }
}

class UniformElement(
  str: String
) extends Element {
  val s = str
  println("=== UniformElement.s " + s)
  def contents = Array(s) // error
  //def contents = Array(str) // ok
}
val e = new UniformElement("str")
println(e.someProperty)

For some reason the initialization of superclass occures before s initialisation:

scala example.scala
=== Element
=== UniformElement.s str
null

Why does the alternative work without s (see commented line in code)?

2 Answers 2

4

The issue is field values are null until constructor completes, and super constructor is referencing indirectly value s which is initialised by child constructor, but the child constructor has not yet completed. The situation looks something like this

class UniformElement {
  def <init>(str: String) = {
    super.<init>()
    s = str
  }
}

where we can see if we replace super.<init>() with

val someProperty: String = {
  println("=== Element")
  contents(0)
}

that it executes before

s = str

Initialisation order issues can often be addressed by changing eager val s into lazy like so

class UniformElement(str: String) extends Element {
  lazy val s = str
  println("=== UniformElement.s " + s)

  def contents = Array(s)
}

which now outputs

=== Element
=== UniformElement.s str
str
Sign up to request clarification or add additional context in comments.

Comments

3

Thanks for the interesting question ! My guess (after spending some time on Scastie) would be this order of initialization :

  1. Arguments : in your case, str is the first value to be defined
  2. Parent : in your case, Element
  3. Child : in your case, UniformElement

So, if I try to put it in a single class order, it goes like this :

class UniformElement{
  // Argument init
  val str = "str"
  // Super constructor
  def contents: Array[String]
  val someProperty: String = {
    println("=== Element")
    contents(0)
  }
  // Child constructor
  val s = str
  println("=== UniformElement.s " + s)
  def contents = Array(s) // error
  //def contents = Array(str) // ok
}

The trick is that, to init someProperty, scala need to evaluate contents(0) and find contents definition. But when finding definition, s is not yet defined (and str is).

So final 'runtime' process:

class UniformElement{
  // Argument init
  val str = "str"
  // Super constructor with contents replaced by definition
  val someProperty: String = {
    println("=== Element")
    Array(s)(0) // error : s doesn't exists !
    // Array(str)(0) // ok : str exists
  }
  // Child constructor
  val s = str
  println("=== UniformElement.s " + s)
  def contents = Array(s) // error
  //def contents = Array(str) // ok
}

To convince yourself, you can try :

println(e.someProperty) // null => s wasn't defined
println(e.contents(0)) // str => s is now defined

Feel free to ask for clarification if needed.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.