People say C++ inheritance is evil, so Java 'fixed' this problem with interface.
But Scala introduced trait
s, they're... interface with partial implementation? Doesn't this brought multiple inheritance back?
Does it mean Scala guys think multiple inheritance is good? Or They have some critical differences I haven't noticed?
Mixins are sometimes described as being "included" rather than "inherited". In short, the key difference from an inheritance is that mix-ins does NOT need to have a "is-a" relationship like in inheritance. From the implementation point of view, you can think it as an interface with implementations.
A mixin is a special kind of multiple inheritance. There are two main situations where mixins are used: You want to provide a lot of optional features for a class. You want to use one particular feature in a lot of different classes.
Multiple Inheritance Multiple inheritance is the type of inheritance where the subclass inherits directly from more than one class. In Scala, this is not achievable with classes. Instead, multiple inheritance is supported via traits.
Traits are compile-time external values (rather than code generated from an external source). The difference is subtle. Mixins add logic, Traits add data such as compile-time type information.
The worst part of multiple inheritance is diamond inheritance, where a subclass has two or more paths to the same parent somewhere up the chain. This creates ambiguity if implementations differ along the two paths (i.e. are overridden from the original implementation). In C++ the solution is particularly ugly: you embed both incompatible parent classes and have to specify when you call which implementation you want. This is confusing, creates extra work at every call site (or, more likely, forces you to override explicitly and state the one you want; this manual work is tedious and introduces a chance for error), and can lead to objects being larger than they ought to be.
Scala solves some but not all of the problems by limiting multiple inheritance to traits. Because traits have no constructors, the final class can linearize the inheritance tree, which is to say that even though two parents on a path back to a common super-parent nominally are both parents, one is the "correct" one, namely the one listed last. This scheme would leave broken half-initialized classes around if you could have (completely generic) constructors, but as it is, you don't have to embed the class twice, and at the use site you can ignore how much inheritance if any has happened. It does not, however, make it that much easier to reason about what will happen when you layer many traits on top of each other, and if you inherit from both B
and C
, you can't choose to take some of B
's implementations and the some of C
's.
So it's better in that it addresses some of the most serious criticisms of the C++ model. Whether it is better enough is a matter of taste; plenty of people even like the taste of C++'s multiple inheritance well enough to use it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With