Image Blog Java 8 Default Methods
June 17, 2014

Using Java 8 Default Methods

Java Application Development

Today I want to talk about using Java 8 default methods in interfaces. Note, that this post is not about the pitfalls of a certain aspect of the Java language, instead it has a more philosophical takeaway: use new features with caution, don’t rewrite your system using only interfaces just because you can, and think about those who will have to read the code after you. In general, lots of sad pandas are encountered along the way of changing your codebase for the sake of coolness and novelty. Trust me!

It’s been some time now since Java 8 was released into general availability. We’ve now gathered some experience with it, got a chance to apply some cool new features to projects of different complexities. Some people are brave enough to use it now, as they told us themselves in their Java Tools and Technology Landscape for 2014, others are more careful. But sooner or later Java 8 will slip in production environments.

There were 3 major features in the Java 8 release that got everyone talking: Lambdas, the Streams API and Default Methods in interfaces. There are others as well, and one might call Streams just an API, but undoubtedly, these are the big trinity to pay attention to.

Anyway the default methods in interfaces were introduced to the Java language in order to evolve the collections library for the Streams API. Default methods allow an interface in addition to declaring methods actually supply their implementations. So all the subclasses implementing the interface but lacking the implementation of the freshly added methods won’t break right then and there. If you want to refresh your memory, check this detailed blogpost on default methods in Java 8 by the ZeroTurnaround product lead for JRebel, Anton Arhipov.

Image of sad panda who doesn't want to look at us

Rules for Resolving a Method Implementation

The rules for resolving a method implementation are quite straightforward:

  • A concrete implementation in the class wins. 
  • The lowest implementation in the implemented interfaces wins. 
  • If there are multiple implementations available through different interfaces that are on different paths up through the class hierarchy, the program doesn’t compile.

Now, I DO in fact think this is a great contribution to the Java language, but it is also rather interesting to consider how developers can easily, if not unwittingly, abuse default methods if given the chance.

One of the things that come to mind when we talk about default methods in interfaces is Traits. A trait is a piece of functionality that can be declaratively added to a type. Sounds awesome doesn’t it? I have a container with the random access, I add Sortable trait aka the following interface:

public interface Sortable {

    T get(int idx); 
    void set(T t, int idx); 
   void sort() default {
      // here is an implementation of sort using get() and set()
      // maybe I need a size() as well, but that’s not the point

Amazing, now every container I mark with Sortable now supports sorting. But beware: things can easily go wrong when I have a complex type system and several libraries present.

Managing Multiple Libraries in a Complex Type System

For example, imagine that you have four types, two classes and two interfaces:

package org.shelajev.throwaway.defmeth;

public class Main {
  public static void main(String[] args) {
    // what will this program produce?
    B b = new B();


    A a = new B();

class A implements I1 {
  public void callM() {

class B extends A implements I2 {
  public void callSuperM() {

interface I1 {
  default void m() {

interface I2 extends I1 {
  default void m() {

Now the caveat here is that when we have an instance of A, we don’t really know which implementation of m() will be called from the callM() call.

Naturally, we would expect that to come from an interface, because no class provides a concrete implementation themselves; however, our class B here implements a sneaky interface I2, which overrides / overloads (?) the implementation and makes things fuzzy.

The program above actually outputs the following:


This is easy to see in this synthetic tiny example, but in a real world system, reasoning about the program flow will be much harder.

The worst thing is that your IDE cannot help you here too. Until runtime kicks in, you cannot be sure what implementation sits behind the generic interface type variable. So, in addition to 16 layers of interfaces calling each other that we so often encounter in the enterprise applications, we will have to crawl through a jungle of default methods in these interfaces.

Now, I understand that this kind of code is not so common in your average application, however, if you encourage the use of default methods as traits, you will get those problems.

And if you’re not convinced, check out this older thread from the openjdk mail list, where Brian Goetz explains some reasons why default methods were designed the way they were.

When to Use Java 8 Default Methods in Interfaces

To conclude, I would like you to forget about the default methods in interfaces until the moment you find yourself with a widely popular, but somewhat outdated library that you want to evolve. Then the default methods are the right tool for the job. Until then, go with a single inheritance and make people reading your code happier!

If you have an opinion, don't hesitate to leave a comment or find me on Twitter: @shelajev.

If you're interested in the Java 8 vs Java 7 performance benchmark, check out that post as your next read! Alternatively also look at how you can use Java 8 Streams, filters, maps and foreach in another amazing RebelLabs post.

Default methods are really cool, but should be used sparingly. Find out more about the new features in Java 8 with "Java 8 Revealed: Lambdas, Default Methods and Bulk Data Operations".

Get the Report