Imagine a bacon-wrapped Ferrari. Still not better than our free technical reports.
See all our reports

Java 8 explained: Default Methods

UPDATE: Since releasing this post, we’ve published a full Rebel Labs report entitled Java 8 Revealed: Lambdas, Default Methods and Bulk Data Operations. Click on the button below to get the full report!

Get the complete Rebel Labs report



In a previous post, we looked into lambdas in Java 8 which proved to be a very popular topic. Now we continue our peek into new Java 8 features by continuing with default methods. Those are closely related to lambdas, which could be the main theme of Java 8. In this article, we’ll take a look at what default methods are, what are the gotchas in using the default methods and how to apply the new feature in your daily life.

Why default methods?

Java 8 is approaching and even though the deadline has been pushed back, we can be quite confident that it will bring lambdas when it is finally out. As stated above, we did already cover the subject a bit some time ago, however, lambdas alone are not the only game-changer in Java 8.

Suppose Java 8 is out and has lambdas. Now you would like to start using lambdas and the most obvious use case for that is to apply a lambda to every element of a collection.

List<?> list = …
list.forEach(); // lambda code goes here

The forEach isn’t declared by java.util.List nor the java.util.Collection interface yet. One obvious solution would be to just add the new method to the existing interface and provide the implementation where required in the JDK. However, once published, it is impossible to add methods to an interface without breaking the existing implementation.

So it’d be really frustrating if we had lambdas in Java 8 but couldn’t use those with the standard collections library since backwards compatibility can’t be sacrificed.

Due to the problem described above a new concept was introduced. Virtual extension methods, or, as they are often called, defender methods, can now be added to interfaces providing a default implementation of the declared behavior.

Simply speaking, interfaces in Java can now implement methods. The benefit that default methods bring is that now it’s possible to add a new default method to the interface and it doesn’t break the implementations.

In my opinion, it doesn’t seem to be the language feature that would be appropriate to use every day, but it seems to be essential for Java Collections API update to be able to use lambdas naturally.

Download the Full Report (pdf)

Defenders 101

The most trivial example

Let’s start with the simplest example possible: an interface A, and a class Clazz that implements interface A.

public interface A {
    default void foo(){
       System.out.println("Calling A.foo()");
    }
}
 
public class Clazz implements A {
}

The code compiles even though Clazz does not implement method foo(). Method foo() default implementation is now provided by interface A.

And the client code that users the example:

Clazz clazz = new Clazz();
clazz.foo(); // Calling A.foo()

Multiple inheritance?

There is one common question that people ask about default methods when they hear about the new feature for the first time: “What if the class implements two interfaces and both those interfaces define a default method with the same signature?”. Let’s use the previous example to illustrate this situation:

public interface A {
    default void foo(){
       System.out.println("Calling A.foo()");
    }
}
 
public interface B {
    default void foo(){
       System.out.println("Calling B.foo()");
    }
}
 
public class Clazz implements A, B {
}

This code fails to compile with the following result:

java: class Clazz inherits unrelated defaults for foo() from types A and B

To fix that, in Clazz, we have to resolve it manually by overriding the conflicting method:

public class Clazz implements A, B {
    public void foo(){}
}

But what if we would like to call the default implementation of method foo() from interface A instead of implementing our own. It is possible to refer to refer to A#foo() as follows:

public class Clazz implements A, B {
    public void foo(){
       A.super.foo();
    }
}

Now I’m not quite sure that I like the final solution. Maybe it would be more elegant to specify the default method implementation in the signature, as it was specified in the first drafts of the default methods specification:

public class Clazz implements A, B {
    public void foo() default A.foo;
}

But it actually changes the grammar, doesn’t it? It looks more like a method declaration of an interface rather than the implementation form. But what if interface A and interface B define a lot of conflicting default methods and I’d like to resolve all the default methods from interface A? Currently, I’d have to resolve the conflicts one by one, overriding each conflicting method pair. That might be a lot of work and a lot of boilerplate code to write.

I guess there could be a lot of arguments for and against this way of resolving the conflicts but it seems that the creators just decided to accept the necessary evil.

Real examples

The real examples of the the default method implementations can be found in the early builds of JDK8. Going back to the example of forEach method for collections, we can find its default implementation in java.lang.Iterable interface:

@FunctionalInterface
public interface Iterable<T> {
    Iterator<T> iterator();
 
    default void forEach(Consumer<? super T> action) {
    	Objects.requireNonNull(action);
    	for (T t : this) {
        	action.accept(t);
    	}
    }
}

The forEach method takes java.util.function.Consumer functional interface type as a parameter which enables us to pass in a lambda or a method reference as follows:

List<?> list = …
list.forEach(System.out::println);

Method invocation

Let’s take a look on how the default methods are actually invoked. If you are not familiar with the subject, the Rebel Labs report on Java bytecode might be an interesting read for you.

From the client code perspective, default methods are just ordinary virtual methods. Hence the name – virtual extension methods. So in case of the simple example with one class that implements an interface with a default method, the client code that invokes the default method will generate invokeinterface at the call site.

A clazz = new Clazz();
clazz.foo(); // invokeinterface foo()
 
Clazz clazz = new Clazz();
clazz.foo(); // invokevirtual foo()

In case of the default methods conflict resolution, when we override the default method and would like to delegate the invocation to one of the interfaces the invokespecial is inferred as we would call the implementation specifically:

public class Clazz implements A, B {
    public void foo(){
       A.super.foo(); // invokespecial foo()
    }
}

Here’s the javap output:


public void foo();
Code:
0: aload_0
1: invokespecial #2 // InterfaceMethod A.foo:()V
4: return

As you can see, invokespecial instruction is used to invoke the interface method foo(). This is also something new from the bytecode point of view as previously you would only invoke methods via super that points to a class (parent class), and not to an interface.

Finally…

Default methods are an interesting addition to the Java language–you can think of them as a bridge between lambdas and JDK libraries. The primary goal of default methods is to enable an evolution of standard JDK interfaces and provide a smooth experience when we finally start using lambdas in Java 8. Who knows, maybe in the future we will see some more applications of default methods for API design.

In the meantime, stay tuned for more on Java 8 from Rebel Labs! Feel free to leave comments below or tweet us @RebelLabs.


Get the complete Rebel Labs report


  • Marc N

    Then if you could implement default methods in interfaces, wich will be then (in java 8) the differences between interfaces and abstract classes?
    Is this just a tweak to don’t break compatibility

  • arhan

    The difference will be minimal indeed. You can inherit only one abstract class, but you can implement multiple interfaces. Plus, you can’t declare mutable fields in interfaces, which you can do in abstract classes.

    Imo, this is an enabling feature. And yes, not to break the compatibility. However, this feature might be quite useful for API changes for any libraries in the future.

    Well, certainly it would be nice to see real extension methods in Java. But I guess this is not something what Java creators have in plans.

  • Marc N

    Thanks for the explanation.

  • Paul
  • Paul Duffin

    Default methods really only provide a one shot opportunity to add methods to an existing interface and then only for libraries provided with the JDK.

    Lets say that the forEach method is added to Collection. That won’t clash with other code that provides default methods as there will be no other code that can use default methods. However, once Java 8 is released then you cannot add any more default methods to Collection as they could clash with default methods provided by other interfaces. e.g. Say I have my own class that implements Collection and another interface OtherInterface. Before Java 8 is released the OtherInterface cannot contain any default methods so no possibility of clash, after Java 8 is released then OtherInterface could have its own default methods. When Java 9 is released it could not add additional default methods to Collection as they could clash with the default methods provided by my OtherInterface.

    The same issue applies for libraries that are not released as part of Java 8 because they do not know whether their default methods would clash with others.

  • Holger

    This is not a new problem. You can always have a method in a class which conflicts with an inherited method of a future version of its superclass. The JDK developers never stopped adding new methods to a class just because of potential conflicts with 3rd party subclasses. So it’s the same for interface default methods. There will be new methods if they are really useful.

    And to me it looks more like solving problems than creating new ones. I’m looking forward to a future with no more AbstractMethodErrors in JDBC due to interfaces newer than the existing drivers. Just one example…

  • Paul Duffin

    I am not saying it is not a good thing, I was just pointing out its limitations. It definitely does solve a problem, albeit not completely but probably as well as is possible. It changes what happens when adding methods to an interface from “will most likely break existing implementations” (it is not guaranteed as an existing implementation could already implement the added method itself) to “will most likely NOT break existing implementations”.

  • CriticalThinker

    I think if they’d just made multiple inheritance work this way with classes in the first place we wouldn’t need ‘interfaces’ to be something separate.

  • Brent Atkinson

    The problem you’re describing is one that is outlined in more detail. Draw your own conclusions. Personally, I think changing the collections classes to implement an extended interface would have been more appropriate:

    http://fmrldev.blogspot.com/2013/08/normal-0-false-false-false-en-us-x-none.html