Andreas Solymosi

Superiority of Scala’s Trait over Java’s Interface

Former C++-programmers in the beginning miss multiple inheritance for classes in Java. But they learn soon that mostly it’s just for convenience. Java’s support for multiple inheritance among interfaces is sufficient in many cases and if not, they find out how to simulate multiple inheritance by delegation – it’s less convenient but it works.

Let’s assume you’d like to inherit from two classes Super1 and Super2:

class Sub extends Super1, Super2 {} // not Java!

Then you have to decide either for

class Sub implements InterfaceOfSuper2 extends Super1 {

    InterfaceOfSuper2 super2 = new Super2();

… }

or for

class Sub implements InterfaceOfSuper1 extends Super2 {

    InterfaceOfSuper1 super1 = new Super1();

… }

The not inherited methods (and other exports) must be passed through, cumbersome – it’s inconvenient but better than no solution:

class Sub implements InterfaceOfSuper2 extends Super1 {

    InterfaceOfSuper2 super2 = new Super2();

void methodFromSuper2() {

    super2.methodFromSuper2(); }

SomeType functionFromSuper2() {

    return super2.functionFromSuper2(); }

… }

Typically you should decide for the more convenient superclass: the one exporting more methods – then you have to type less.

Scala also doesn’t support multiple inheritance among classes, but among traits. A trait is something like an abstract class but with multiple inheritance. Or else: A trait is something like a Java interface but with possible concrete members.  Actually there is only one reason to write an abstract class in Scala rather than a trait: if you want it to be extended by a Java class. If you don’t ever expect a Java class inherit from it, you should always prefer a trait. A trait can do anything an abstract class can (but being extended by a Java class), plus, multiple inheritance:

class Sub extends Super1 with Super2 // Scala

or (with no substantial difference)

class Sub extends Super2 with Super1 // Scala

(assuming Super1 and Super2 have been defined as traits).

If Super1 and Super2 are not only (uninstantiable) traits but also have abstract members then they can be programmed as abstract classes in Java – and they cannot be instantiated. Then simulation (as above) does not work. In these cases problems can be solved C++ and Scala; in Java they can be solved only by changing one of the superclasses. If you don’t have the source code, you have to give up.

An example enlightens the issue.

Let’s assume we have several implementations with different technologies (array, linked list, etc.) of the (generic) interface Queue with the usual methods add, remove, etc.:

class ArrayQ<E> implements Queue<E> { … } // Java

class ListQ<E> implements Queue<E> { … }

or

class ArrayQ[E] extends Queue[E] { … } // Scala

class ListQ[E] extends Queue[E] { … }

An extension of the interface (in Scala: of the trait) Queue with additional methods (like copy and equals) can be implemented through inheritance from ArrayQ and ListQ:

class ExtArrayQ<E> extends ArrayQ<E> implements ExtQ<E> { … } // Java

class ExtListQ<E> extends ListQ<E> implements ExtQ<E> { … }

respectively

class ExtArrayQ[E] extends ArrayQ[E] with ExtQ[E] { … } // Scala

class ExtListQ[E] extends ListQ[E] with ExtQ[E] { … }

Other extensions of the interface Queue can have other additional methods; for example a Persistent Queue has methods like save and load:

interface PersQ<E> extends Queue<E> { // Java

    void save(String filename);

    void load(String filename); }

and

trait PersQ[E] extends Queue[E] { // Scala

    def save(filename: String)

    def load(filename: String) }

These methods can be implemented in different ways like a binary file (through an ObjectIn/Out­put­Stream) or an XML file:

class PersArrayObjectQ<E> extends ArrayQ<E> // Java

    implements PersQ<E>, Serializable {

    … } // implementation of save and load with ObjectIn/OutputStream

class PersArrayXMLQ<E> extends ArrayQ<E> implements PersQ<E> {

    … } // implementation of save and load with XML

// similarly for ListQ

All these implementations (also equals and copy) have one common algorithm: loop through the queue and manipulate the elements. The idea of functional abstraction is to bin out the algorithm to an iterator:

class ItArrayQ<E> extends ArrayQ<E> implements Iterable<E> { // Java

    public Iterator<E> iterator() { … } } // iterator for array

class ItListQ<E> extends ListQ<E> implements Iterable<E> {

    public Iterator<E> iterator() { … } } // iterator for list

and then the extensions can be easily implemented through inheritance from ItArrayQ and ItListQ:

class ExtItArrayQ<E> extends ItArrayQ<E> implements ExtQ<E> { // Java

    … } // copy and equals implemented with iterator

class ExtItListQ<E> extends ItListQ<E> implements ExtQ<E> {

    … } // copy and equals implemented with iterator

Of course, the two class bodies above are the same: functional abstraction works. To complete it, we create an abstract class containing the abstract (technology independent) algorithm:

abstract class ExtItQ<E> implements ExtQ<E>, Iterable<E> { // Java

  public boolean equals(ExtQ<E> q) {

    Iterator<E> thisOne = this.iterator(),

       thatOne = ((ExtItQ<E>)q).iterator();

    while (thisOne.hasNext() && thatOne.hasNext())

       if (thisOne.next() != thatOne.next())

         return false;

    return !thisOne.hasNext() && !thatOne.hasNext(); }

  public void copy(ExtQ<E> q) {

    this.clear();

    Iterator<E> thatOne = ((ExtItQ<E>)q).iterator();

    while (thatOne.hasNext())

      this.add(thatOne.next()); } }

This class is abstract because it doesn’t implement[1] Iterable.iterator – this would be a technology dependent method. The same can be done for PersItQ.

The solution can be expressed in Scala in a very similar but more concise way:

trait ExtItQ[E] extends ExtQ[E] with Iterable[E] { // Scala

  def equals(that: ExtQ[E]) = {

    val thisOne = this.iterator

    val thatOne = that.asInstanceOf[ExtItQ[E]].iterator

    while (thisOne.hasNext && thatOne.hasNext)

       if (thisOne.next != thatOne.next)

         false

    !thisOne.hasNext && !thatOne.hasNext }

  def copy(q: ExtQ[E]) = {

    this.clear

    val thatOne = q.asInstanceOf[ExtItQ[E]].iterator

    while (thatOne.hasNext)

       this.add(thatOne.next) } }

trait ItArrayQ[E] extends ArrayQ[E] with collection.Iterable[E] {

    def iterator = … } // iterator for array

trait ItListQ[E] extends ListQ[E] with collection.Iterable[E] {

    def iterator = … } // iterator for list

In Scala – thank to multiple inheritance among traits – we can mix in the iterator implementation with the implementation of equals/copy and we receive a concrete class without no additional programming:

class ExtItListQ[E] extends ItListQ[E] with ExtItQ[E] // Scala

class ExtItArrayQ[E](size: Int) extends ArrayQ[E](size) // [2]

    with ItArrayQ[E] with ExtItQ[E]

In Java, however, we’ve got trouble:

class ExtItArrayQ<E> extends ExtItQ<E>, ItArrayQ<E> {} // not Java!

Since Java doesn’t support multiple inheritance we have to decide, which superclass to instantiate. ItArrayQ would be nice to keep as superclass since it exports more methods, but ExtItQ is abstract, it cannot be instantiated. So perforce we have to work cumbersome:

class ExtItArrayQ<E> extends ExtItQ<E> { // Java

    private ItArrayQ<E> iaq;

    public ExtItArrayQ1(int size) { iaq = new ItArrayQ<E>(size); }

    public void add(E o) { iaq.add(o); }

    public void remove() { iaq.remove(); }

    … // equals and copy inherited from ExtItQ

    public Iterator<E> iterator() { return iaq.iterator(); } }

class ExtItListQ<E> extends ExtItQ<E> … // similar

However, this is luck: one of the two superclasses is not abstract. If both are abstract, none of them can be instantiated. In this case there is no way in Java to solve the problem without changing them.

Let’s assume, we performed functional abstraction for Extended Queue and Persistent Queue like above. Now we need a Universal Queue capable of everything:

interface UnivQ<E> extends ExtQ<E>, PersQ<E> // Java

or in Scala:

trait UnivQ[E] extends ExtQ[E] with PersQ[E] // or vice versa – no difference

In Scala we implement it easily through multiple inheritance:

class UnivArrayQ[E](size: Int) extends ArrayQ[E](size)

    with ItArrayQ[E] with ExtItQ[E] with PersItQ[E] with ManItQ[E]

class UnivListQ[E] extends ItListQ[E]

    with ExtItQ[E] with PersItQ[E] with ManItQ[E]

A class diagram expresses the dependencies among these types:

Class diagram

In this class diagram we presented interfaces (traits with only abstract members) and interface extensions (inheritance of abstract members) red, implementations of abstract members green; black arrows represent inheritance of concrete members. Names of abstract classes and interfaces are italic (as usual).

The top of the hierarchy is Queue, extended by ExtQ and PersQ, extended by UnivQ, implemented by UnivArrayQ and UnivListQ (at the bottom). ExtQ and PersQ are partially implemented by ExtItQ resp. PersItQ; these two inherit the (only) abstract method from Iterable. They are technology independent implementations of the interfaces ExtQ resp. PersQ. Technology dependency comes in with the classes ArrayQ and ListQ (implementing Queue), extended by ItArrayQ and ItListQ (implementing Iterable). UnivArrayQ and UnivListQ mix in ItArrayQ (resp. ItListQ) with ExitItQ and PersItQ.

If we try to implement the same structure in Java, we find unfortunately that two of the three superclasses are abstract: ExtItQ and PersItQ – we can instantiate only one (ItArrayQ).

This problem cannot be solved in Java without changing a superclass. We have to make them to inherit from each other:

abstract class ExtItQ1<E> extends PersItQ<E> // changed!
implements ExtQ<E>, Iterable<E> { … } // like above

class UnivArrayQ<E> extends ExtItQ1<E> implements UnivQ<E> {

private ItArrayQ<E> iaq; … } // like above

class UnivListQ<E> extends ExtItQ1<E> implements UnivQ<E> {

private ItListQ<E> ilq; … } // like above

The following class diagram presents the dependency of types in the Java version:

Java Class Diagram

Lack of multiple inheritance UnivArrayQ and UnivListQ have to instantiate (delegate to) ItArrayQ (resp. ItListQ: diamond arrow). Because ExtItQ1 (unfortunately perforce) extends PersItQ, they inherit from both.

The arrow leading from ExtItQ1 to PersItQ is very undesirable and perforce; it destroys independence of implementations and is retroactively necessary only for substitution of multiple inheritance among abstract classes.


Version: 27. Dezember 2010

© Prof. Solymosi, 2010, Beuth-Hochschule für Technik Berlin, Faculty for Computer Science and Media

solymosibht-berlin.de


[1] Java’s syntax with implements expresses in this case the opposite fact: inheritance of an abstract method

[2] Here we have to name ArrayQ as superclass in order to pass class parameter (usually needed for array implementations: the length of the array). ListQ (without class parameter) inherits it from ItListQ.