1
votes

I am referring book "Scala for the Impatient" for learning purpose. I found some example of construction order problem and solution of early definition.

The book has tried to showcase the problem with following class :

class Creature {
  val range: Int = 10
  val env: Array[Int] = new Array[Int](range)

}

class Ant extends Animal {
  override val range: Int = 2
}

Here, as per the construction order problem, while creating an instance of class Ant, the super class Creature constructor is called and the env array is initialised with default Int value zero.

To solve the problem, they have mentioned three possible solutions :

  1. Declare the val as final
  2. Declare val as lazy in super class
  3. Use early definition syntax

It is easy to understand the point#2 and point#3 logically.

But how can we solve this problem by declaring the range field as final in sub class ?

I tried with following solution and it worked. But I am not able to logically relate here how overriding a field as final in subclass can solve this ?

package ed2 {

  class Creature {
    val range: Int = 10
    val env: Array[Int] = new Array[Int](range)
  }

  class Ant extends Creature {
    final override val range = 2
  }
}

Note : One important point to note here is, we are able to produce this behaviour while deferring the type declaration of the final overridden field in subclass final override val range = 2. This code snippet returns zero if we declare field type in subclass.

How the compiler treats final fields of subclass ? Does it gives first priority to final members for initialisation ?

1
I have tried to replicate the problem here : scastie.scala-lang.org/p5fXFRejQAW3RGcdvMjbPgGunjan Shah
A simpler illustration of the issue here: scastie.scala-lang.org/E6xL9zoxQcupcZguNtKEjgSwiftMango

1 Answers

2
votes

Your book is correct but not completely. There is a type of expression in Scala called Constant Value. It is inlined everywhere it is used:

The final modifier must be present and no type annotation may be given. References to the constant value x are themselves treated as constant expressions; in the generated code they are replaced by the definition's right-hand side e.

So in short,

  1. It requires final keyword
  2. It requires no type annotation

Then they are inlined in the class definition and therefore it is 2 in your Ant class.

In other cases where you supply a type definition like final override val range: Int = 2, then it is not considered a constant value and not inlined. Therefore it is initialized according to the regular order of inheritance.