Mixin Class Compositions in Scala
Scala allows a class to inherit from other classes. With this inheritence, it can also mix-in behaviors from other classes. This allows additional behavior to be added to the classes. Traits can be added to provide generalization of orthogonal behaviors then mixed-in to the class definitions.
Here we have introduced two traits, Walker and Runner. A person can be both a walker and runner, so we are mixing in these two traits to our Person class definition.
Here we are instantiating the Person class defined above. You can notice that we are using run and walk methods which have been mixed-in from Runner and Walker traits respectively.
Mixin compositions can also be used to define singleton objects. Here we have Person object using the mixin compositions using Walker and Runner. You can also notice the syntax to mixin multiple traits with their individual with keyword.
We can mixin traits while instantiating classes with added members from mixin traits. A human object would have everything Human class defines. Additionally, it can use members from Walker and Runner traits.
It can also be a case class. Here we have turned Human into a case class with name field. We can see that we are mixing in members from Walker and Runner while instantiating a Human.
Scala just allows traits mixins and we cannot mixin classes. An attempt to do so would result in a compile time error as follows:
Traits Mixins with conflicting members
It is possible that traits have conflicting members. In the following example there are two traits Fixer and Doer with the same member doWork. Now which of these doWork would be picked up when we invoke it from the instance.
Actually Scala doesn’t like this conflict while mixing-ins. It simply results in a compile time error.
As the message suggests we need to override the conflicting members to get rid of the error. Let’s override this and provide a definition of the doWork method.
This makes compiler happy. But there is an interesting thing in the above code. We have called super.doWork(). So would it use it from Doer or Fixer. Actually scala has a straitforward policy to this, it would use the conflicting members from right to left while mixing in. Since we have Doer on the extreme right, it would use doWork from Doer.
Required Mixin for Instantiation
Scala also provides the support for mandatory trait mixing for a class instantiation. This is specially useful when we have more abstract definitions of trait and a more concrete trait is expected for the instances of the class.
In the above example, we have introduced a class Worker. It requires Fixer to be instantiated. We have also introduced a new trait AwesomeFixer which extends Fixer.
As we add the mixin for when instantiated, the compiler error goes away.
Refinements with Mixins
While doing the mixins, we can do further refinements. In the following example, we have introduced a case class XYZ with a field fullName. There is a method perform, which has a parameter performer specified in terms of a structural type with members name (field) and doWork (method). We have created instantiated obj from XYZ mixed in with Worker. Still it doesn’t fulfill the method parameter structural requirement. We can refine this with refinement by creating a new field name and assign it the fullName property from XYZ.