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.
Bar
- If necessary, a class in the hierarchy overrides this behavior e.g.
Corge
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.AbstractList
and 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 sort()
of AbstractList
.
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 static
methods.
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 List
to 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 Collections.sort()
implementation has been rewritten to delegate to the default
method:
// Without generics for better readability public class Collections { public static void sort(List list) { list.sort(null); } }
Conclusion
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.
Author: Nicolas Fränkel
Nicolas Fränkel is a Developer Advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). Usually working on Java/Java EE and Spring technologies, but with focused interests like Rich Internet Applications, Testing, CI/CD and DevOps. Also double as a teacher in universities and higher education schools, a trainer and triples as a book author.
You can find Nicolas’ weekly post on his blog.