Resolves the default method in Java

  • 2020-04-01 03:58:13
  • OfStack

  Why there are default methods ?

Java 8 is coming, and although the release deadline has been pushed back, we are pretty sure that lambdas expressions will be supported when it is finally released. As mentioned, we've talked a lot about this topic before, but lambdas expressions aren't the only game changer in Java 8.


Assume that Java 8 has been released and includes lambda. Now that you're ready to use lambda, the most obvious scenario is to apply lambda to each element of the collection.
 


List<?> list =  ... 
list.forEach( ... ); //So that's lambda code

The forEach definition cannot be found in the java.util.list or java.util.collection interfaces. A common solution is to add new methods and implementations to the associated interfaces in the JDK. However, with a released version, there is no way to add new methods to the interface without affecting the existing implementation.

So, how bad would it be to use lambda in Java 8 and not be able to use the collection library because of forward compatibility?


For the above reasons, a new concept was introduced. The virtual extension method, commonly known as the defender method, can now be added to the interface to provide a default implementation of the declared behavior.

Simply put, the Java interface can now implement methods. The advantage of default methods is that you can add new default methods to the interface without breaking the implementation of the interface.

This isn't the kind of Java feature that I see every day, but it definitely makes it natural for the Collections API to use lambda.

The simplest example

Let's look at the simplest example: an interface A, which the Clazz class implements.
 


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

The code compiles, even if the Clazz class does not implement the foo() method. The default implementation of the foo() method is provided in interface A.

Client code using this example:
 


Clazz clazz = new Clazz();
clazz.foo(); //Call A.f oo ()

Multiple inheritance?

A common question people ask when they first hear about the new features of default methods is "what if a class implements two interfaces and both interfaces define default methods with the same signature?" Let's use the previous example to demonstrate this solution:
 


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 does not compile for the following reasons:

Java :class Clazz inherits irrelevant default values from types A to B to foo()

To fix this, in Clazz we had to manually resolve the conflict by rewriting:
 


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

But what if we want to call the default implementation method foo() from interface A instead of implementing our own method? This is possible by referring to foo() in A, as follows:
 


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

Now I'm not quite sure I like the final plan. Perhaps it is more concise than declaring the default method implementation in the signature, as stated in the first draft of the default method specification:
 


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

But that does change the syntax, doesn't it ? It looks more like a method declaration for an interface than an implementation. What if interface A and interface B define many conflicting default methods, and I'm willing to use all of interface A's default methods to resolve the conflicts? At the moment I have to solve one conflict after another, rewriting the way each pair works. This can require a lot of work and writing a lot of template code.

I expect a lot of discussion about how to resolve conflicts, but it seems that the creators have decided to accept the inevitable.

Real examples

A real-world example of the default method implementation can be found in the early JDK8 package. Returning to the example of the forEach method of the collection, we can see that in the java.lang.iterable interface, its default implementation is as follows:
 


@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);
    }
  }
}

ForEach USES a Java. Util. The function. The function of Consumer interface type parameter, it allows us to pass in a lambda expressions, or a method reference, as follows:
 


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

The method call
Let's see how the default method is actually called. If you are not familiar with this issue, you may be interested in reading Rebel LABS 'report on Java bytes.

From the point of view of the client code, the default method is just a common virtual method. So the name should be the virtual extension method. So for a simple example class that implements the default method as an interface, the client code will automatically call the interface where the default method is called.
 


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

If the default method conflict is resolved, when we modify the default method and specify one of the interfaces to invoke, invokespecial will give us the implementation of which interface to invoke.
 


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

Here is the output from javap:


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

As you can see: the invokespecial directive is used to invoke the interface method foo(). This is still new from a bytecode perspective, because previously you could only call methods by pointing to a class (parent class) instead of a super pointing to an interface.

And finally...

The default methods are interesting additions to the Java language. You can think of them as a bridge between lambdas expressions and JDK libraries. The main goal of the default expression is to evolve the standard JDK interface and provide a smooth transition experience when we finally start using the lambdas expression in Java 8. Who knows, maybe we'll see more of the default methods used in API design in the future.


Related articles: