Image Blog Java 8 Explained Default Methods
June 5, 2013

How to Use Default Methods in Java 8

Java Application Development
Java Updates

Closely related to lambdas, here we explore how to use default methods in Java 8.  We’ll take a look at what default methods are, the gotchas in using the default methods, and how to apply the feature in your daily life.

Why Default Methods?

The most obvious use case for Java 8 lambdas 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 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 couldn't use lambdas with the standard collections library since backwards compatibility can’t be sacrificed.

Introducing Virtual Extension Methods

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

Simply speaking, interfaces in Java can implement methods. The benefit that default methods bring is that 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.

 

Using Defender Methods in Java 8

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?

One common question that people ask about default methods: "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.

Examples of Default Method Implementations in Java 8

The real examples of 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 {
    Iterator iterator();
    
    default void forEach(Consumer 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); 

 

2022 Java Developer Productivity Report:
Download the latest developer report for key findings on the state of Java.

Method Invocation

Let's take a look on how the default methods are actually invoked. 

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 methodfoo(). 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.

Final Thoughts on Default Methods in Java 8

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. 

If you're developing in Java, skip time-consuming rebuilds and redeploys during development! JRebel lets you avoid the time spent reproducing the pre-change application state after a redeploy. See how much you can accelerate development with a free, 14-day trial of JRebel.

Try JRebel Today