Java

Java 8 explained: Default Methods

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.

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 Bdefine a lot of conflicting default methods and I’d like to resolve all the default methods frominterface 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.

From http://zeroturnaround.com/

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s