Consider the following example of traits:
trait TextileEngineer extends Engineer {
override def nameIt = {println("TextileEngineer"); super.nameIt;}
}
trait FabricEngineer extends Engineer {
override def nameIt = {println("FabricEngineer"); super.nameIt;}
}
trait Engineer extends Person {
override def nameIt = {println("Engineer"); super.nameIt;}
}
trait ClothMaker extends Trader {
override def nameIt = {println("ClothMaker"); super.nameIt;}
}
trait Trader {
def nameIt = println("Trader");
}
trait Parent extends Person with Human {
override def nameIt = {println("Parent"); super.nameIt;}
}
trait Person extends Human {
override def nameIt = {println("Person"); super.nameIt;}
}
trait Human {
def nameIt = {println("Human");}
}
object Linear extends App {
val data = new TheSmiths {}
data.nameIt
}
trait TheSmiths extends FabricEngineer with TextileEngineer with ClothMaker with Parent {
override def nameIt: Unit = super[TextileEngineer].nameIt //-----------override:1
override def nameIt: Unit = super[FabricEngineer].nameIt //------------override:2
override def nameIt: Unit = super.nameIt //------------override:3
}
TextileEngineer
FabricEngineer
Engineer
Person
Human
FabricEngineer
Engineer
Person
Human
Human
when the trait Parent
is the part of other hierarchy?Parent
ClothMaker
Trader
The short answer is because Trader
does not call super
inside nameIt
:
trait Trader {
def nameIt = println("Trader");
}
The long answer is because linearization begins with TheSmiths
itself, then checks every class or trait it inherits from, starting from the rightmost extended trait to the leftmost extended trait (or class if you extend one), but in a depth-first search manner.
So for example, your linearization of TheSmiths
is:
TheSmiths -> Parent -> Human -> Person -> Human -> ClothMaker -> Trader -> TextileEngineer ->
Engineer -> Person -> Human -> FabricEngineer -> Engineer -> Person -> Human
Now duplicates (which indicate a diamond problem) are eliminated leaving the last occurrence of every type in the list.
Let's understand this first through a simpler example. Parent
also has a diamond problem: it inherits Human
once directly through mixing, and another one through extending Person
which also mixes in Human
. So the initial linearization should be:
Parent -> Human -> Person -> Human
Now we have to remove duplicates and keep the last occurrence of every type. So the linearization of Parent
becomes:
Parent -> Person -> Human
As we can see, in this case - writing with Human
- had no effect in the definition of Parent
.
Coming back, using the same pattern, the actual linearization of TheSmiths
becomes:
TheSmiths -> Parent -> ClothMaker -> Trader -> TextileEngineer -> FabricEngineer ->
Engineer -> Person -> Human
This is the linearization that occurs when you instantiate TheSmiths
using new
.
Then, whenever you call
super
inside one of those classes, the invoked method is the next one up the chain. If all of the methods but the last one call super, the net result is stackable behavior. (Programming in Scala)
Linearization also serves as a way of verifying the output of the 2 other examples you gave.