Java 8 introduced default methods in interfaces. This post describes what they are, and how they can change the design of APIs.
A nominal design
Earlier, in Java, interfaces could only have contracts - method signatures with no implementation.
In order to add some implementation, a class was required, whether abstract or not.
Hence, traditional API design then followed this hierarchy:
- The root interface defines the contract
- An intermediate class implements common behavior i.e.
- If necessary, a class in the hierarchy overrides this behavior e.g.
A wrench in the works
This is perfect, until classes outside the reach of the API designer can implement the interface. The following hierarchy describes the
List part of the Java Collections API, with an additional custom class:
Now, let’s introduce the
sort() method in the
List interface. Only classes i.e.
MyList can actually implement this method.
Obviously, it’s impossible to enforce the same
sort() implementation in both classes, even though it makes sense. Direct implementations of
List have to duplicate (yuck!) the
In order to remove the duplication and DRY the design, Java API designers have moved the
sort() method out of
List to an unrelated class with only
This resolves the common code issue, as there’s now only one single method responsible for sorting.
On the flip side,
static methods are not object-oriented. Worse, there’s no relationship from
Collections in the code (though there’s one in the opposite direction). Hence, if one is not aware of the
Collections class and its features, there’s no way to know about it.
Default methods to the rescue
Now, imagine if it were possible to implement code in interface methods. The
sort() method could be implemented in the
List interface. The above class diagram would then look like that:
That would solve the above issue. By default, every list implementation would be provided with the
sort() method by inheritance.
This is exactly the reason for default methods. No more, no less.
For curious reader, the
If you end up having to duplicate code in multiple classes instead of factoring it into a single common interface, a default method is a far more elegant solution than helper classes.