views:

105

answers:

1

Please look at the following code.

trait MyTrait { val myVal : String }

class MyClass extends MyTrait { val myVal = "Value" }

class MyClass2(val myVal: String) extends MyTrait 

Why does the initialization order differ in case of MyClass and MyClass2? The constructor of MyClass will be as

MyClass() {
  MyTrait$class.$init$(this);
  myVal = value
}

The constructor of MyClass2 will be

MyClass2(String myVal) { this.myVal = myVal; MyTrait$class.$init$(this) }

I think the initialization order should be as MyClass2's constructor does, the same for both cases.

+6  A: 

At the end of section 5.1 of the Scala specification, the following is defined:

Template Evaluation. Consider a template sc with mt 1 with mt n {stats}. If this is the template of a trait (§5.3.3) then its mixin-evaluation consists of an eval- uation of the statement sequence stats. If this is not a template of a trait, then its evaluation consists of the following steps.

  • First, the superclass constructor sc is evaluated (§5.1.1).
  • Then, all base classes in the template’s linearization (§5.1.2) up to the template’s superclass denoted by sc are mixin-evaluated. Mixin-evaluation hap- pens in reverse order of occurrence in the linearization.
  • Finally the statement sequence stats is evaluated.

Note, however, that the constructor parameters may be used by any constructors that follow it. Therefore, it needs to be initialized before them. This is made explicit at the end of section 5.1.1:

An evaluation of a constructor invocation x.c targs. . .(argsn) consists of the following steps:

  • First, the prefix x is evaluated.
  • Then, the arguments args1 , . . . , argsn are evaluated from left to right.
  • Finally, the class being constructed is initialized by evaluating the template of the class referred to by c.

This you don't have any problem with, but you do have a problem with {stats} being executed last. The reason why {stats} is executed last is that it may reference attributes of its ancestor classes and traits, whereas the ancestors obviously have no knowledge about its descendants. Therefore, the ancestors need to be fully initialized before {stats} gets executed.

Of course, it is possible that you do need early initialization. This is covered by section 5.1.6: Early Definitions. Here's how you'd write it:

class MyClass extends { val myVal = "Value" } with MyTrait
Daniel
Eventually I've seen the reason why Scala initializes an object in that order. Thanks for the answer, Daniel.
Dmitry