Scala Linearization is a deterministic process which comes into play when an object of a class is created which is defined using inheritance of different traits and classes. linearization helps to resolve the diamond problem which occurs when a class or trait inherits a same property from 2 different concrete classes or traits.
Syntax :
trait C{}
trait B{}
class A{}
object a_obj= new class A extends B with C
The linearization will look like as follows :-
C-> AnyRef-> Any
B-> AnyRef-> Any
A-> AnyRef-> Any
a_obj-> A-> C-> B-> AnyRef-> Any
Here Any is the superclass of all classes, also called the top class. It defines certain universal methods such as equals, hashCode, and toString. AnyRef represents reference classes. All non-value types are defined as reference types. AnyRef corresponds to java.lang.object . Every Scala trait and class implicitly extend these Scala objects at the end of linearization hierarchy.
Examples :Scala
// Scala program defining trait AtraitA{defname:String}// defining trait B inheriting AtraitBextendsA{overridedefname:String="class b"}// defining trait C inheriting AtraitCextendsA{overridedefname:String="class c"}// defining class D inheriting B and C bothclassDextendsBwithC{overridedefname:String=super.name}// Creating objectobjectGFG{// Main methoddefmain(args:Array[String]){varclass_d=newD// whose property will be inheritedprintln(class_d.name)}}
Output :
class c
Linearization for class D follows dark bold arrow. Inheritance for class D follows light arrow.
Trait linearization and inheritance diagram
As we can see in above diagram linearization will not be as same as inherited structure. Scala traits/classes are dynamically placed in linear order in which will the linearization will be applied as below.
D-> C-> B-> A-> AnyRef-> Any
Following rules are followed for the determining the linearization:
Take the first extended trait/class and write its complete inherited hierarchy in vertical form, store this hierarchy as X.
Take the next trait/class after the with clause, write its complete hierarchy and cancel the classes or traits that are repeated in hierarchy X. Add the remaining traits/classes to the front of the hierarchy X.
Go to step 2 and repeat the process, until no trait/class is left out.
Place the class itself in front of hierarchy as head for which the hierarchy is being written.
Let's understand some examples.
Example :Scala
// Scala program for linearization// defining old_car classclassold_Car{defmethod:String="old car "}// defining new_Car_Designs traittraitnew_Car_Designsextendsold_Car{overridedefmethod:String="Designing-> "+super.method}// defining new_Car_Part traittraitnew_Car_Partextendsold_Car{overridedefmethod:String="Add new part-> "+super.method}// defining new_Car_Paint traittraitnew_Car_Paintextendsold_Car{overridedefmethod:String="Repainting-> "+super.method}// defining new_Car classclassnew_Carextendsnew_Car_Paintwithnew_Car_Partwithnew_Car_Designs{overridedefmethod:String="new car-> "+super.method}// Creating objectobjectgeekforgeeks{// Main methoddefmain(args:Array[String]){// new_Car objectvarcar1=newnew_Carprintln(car1.method)}}
Output :
new car-> Designing-> Add new part-> Repainting-> old car
Example :Scala
// Scala program for trait linearization// defining classes and traitsclassflavour{defmake(flavour:String):Unit={println(flavour)}}// defining texture traittraittextureextendsflavour{abstractoverridedefmake(flavour:String){super.make(flavour+"texture ")}}// defining cream traittraitcreamextendstexture{abstractoverridedefmake(flavour:String){super.make(flavour+"with cream ")}}// defining jelly traittraitjellyextendstexture{abstractoverridedefmake(flavour:String){super.make(flavour+"with jelly ")}}// defining cone traittraitconeextendsflavour{abstractoverridedefmake(flavour:String){super.make(flavour+"in cone ")}}// creating new ice-cream flovours // with above traits and classes// inheriting different traits and classesclassMyflavourextendsflavourwithjelly{overridedefmake(flavour:String){super.make(flavour)}}classMyflavour2extendsflavourwithcreamwithcone{overridedefmake(flavour:String){super.make(flavour)}}// Creating objectobjectGFG{// Main methoddefmain(args:Array[String]){// creating new objectsvaricecream1=newMyflavourvaricecream2=newMyflavour2withjellyprintln(icecream1.make("chocolate "))println(icecream2.make("vanilla "))}}
Output :
chocolate with jelly texture
()
vanilla with jelly in cone with cream texture
()
Important Points About Linearization
Scala solves ambiguity of traits/classes by linearization process.
Scala uses linearization whenever a new class has been instantiated. Taking all the traits/classes and forming a linear order which points to corresponding super classes/traits thus super method knows its parent method.
These super method calling is done in a stackable manner.
Linearization may or not be the same as the inherited mixins as they are written.
We cannot explicitly add a class to inheritance when it is already been implicitly inherited in a linearization otherwise it will result in error as inheritance twice.