A couple of days ago, a discussion came up in an uqbar foundation1 mailing list about the Java 8 Interfaces Default Methods. They were named as «mixins», but I corrected them and called «traits without flattening».
After sending the mail, that last sentence kept ringing in my head, so I proposed myself to try to understand why was I calling them like that. But first things first.
First of all, when I say mixin, I’m using the definition given by Bracha and Cook in their paper «Mixin-Based Inheritance»:
A mixin is an abstract subclass that may be used to specialize the behavior of a variety of parent classes.
What does this mean? Some relevant things that came to my mind:
- A mixin is a class, so it can define behaviour and state (i.e. methods and variables).
- In particular, it’s a subclass. We can call
super(even though we don’t know yet who the parent class is)
- It gives us a form of multiple inheritance, and it handles the diamond problem with linearization: The mixin is “embedded” in the inheritance chain, right before other class that understands the message that is implementing.
This is kinda difficult to explain with just some words, so let’s write some code :)
Note: I will be showing mixins in Scala. The problem is that Scala names them «traits», so it can be a little confusing. Please bear with me through this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
The diagram on the right sums up the hierarchy of the code above. So, what happens when we do
As we stated before, the method-call conflict is resolved by linearization. The console output of the evaluation hence is
1 2 3 4
As we can see, the MixinB is executed before MixinA. If we have some conflict, we just put some mixin above the other. In the case of Scala, mixins are linearized from the outside to the inside of the declaration2, as shown in the image on the left. This means that, if we change SomeClass as shown below:
1 2 3 4 5 6
when we call
new SomeClass().m1, what we get is
1 2 3 4
So, what about traits?
I first heard the term trait in Ducasse, Nierstrasz, Scharli, Wuyts and Black’s paper named «Traits: A Mechanism for Fine-grained Reuse».
As we know, classes have two responsibilities: creating instances and defining behaviour. The idea behind the Traits approach it’s to separate those responsibilities (let’s follow the single responsibility principle :) and let the classes as instances generators while traits defines the behaviour.
Things for taking into consideration:
- Methods defined in classes precede the ones defined in traits.
- The methods defined in the trait «have the same semantics» as the method defined in the class (Flattening property). It’s like they were copied into the class.
- All the traits have the same precedence. If a conflict arises, we have to explicitly handle it.
- Traits provides methods, but can require methods too. This allow us to use methods that are not defined in the same trait.
Some code please! (For this I’m going to use Smalltalk)
1 2 3
And we have another trait that uses the described above.
What happens if we want to define
#foo in this new trait, and call
BaseTrait#foo + something else?
We don’t have
super, because traits aren’t classes. The
uses: implies that the methods will be flattened, so there is no class hierarchy. And as far as we know, if we define
#foo we are overriding the method that came with the trait.
In the end, we need some kind of conflict resolution, so we can call both
1 2 3 4 5 6 7 8 9 10 11
And finally, we want a class, because we need to instantiate objects.
Of course, I want all the behavior of the traits described above, but if I just use them I will have conflicts in two methods:
#foo (this one is the easy one to spot) and
#fooBase. Why? because
#fooBase is defined in both traits. It doesn’t matter if it is just an alias, the object that use the trait behaviour will understand
All said, let’s resolve some conflicts.
The #foo problem
The thing is, I want both
TraitB#foo. So I will take the same approach as before and define an alias for each trait:
The #fooBase problem
Again, I want that behaviour in my class, as my
#foo will have to call it. We could take the same strategy and define an alias in both traits, and the magic is done.
But if I declare an alias, I will be having two different methods with the exact same behaviour. I really don’t know if this is bad, but it adds some extra complexity (and I don’t handle complexity very well :P). The other strategy is to exclude the method in the trait. If one trait excludes the method, then there is no conflict at all :)
Therefore, applying both strategies, we have
1 2 3 4
Then, when we call
SomeClass new foo we get
As you can see, I resolved by hand every possible conflict, and it doesn’t matter how do I write the traits in the
TraitA + TraitB is equivalent to
TraitB + TraitA). If I wanted the
TraitB#foo before the
BaseTrait#foo, I would simply change the method
SomeClass#foo and it’s done.
So, we have seen what mixins and traits are. We have now the tools to face the original issue; How can we define, using these terms, the Interfaces default methods?
But since this is already a loooong post, we will answer that question in the next one :). Don’t forget to check the blog!