Java 8 default methods – Basic introduction

Let us assume that you have designed, published a library and you have many clients who use your library. You have followed the “Object Oriented” concepts and hence made use of interfaces, abstract classes, util classes to design your library.

After a few years, you realize that the core library is quite old, they need to move ahead with time and hence you would like to add new features to this library. At the same time you do not want your existing clients knocking at your door at 2:00 am in the morning to complain about a new piece of functionality you added which broke their existing code.

What are the possible solutions to this?

  1. Have a new interface define these methods, introduce an abstract class  to implement the interface and provide some skeletal behavior for the new functionality. This is actually a design technique that has been used in the Collection framework – The problem here is that we have abstract classes which mean that the existing client cannot extend from more than 1 class. 
  2. Classes with static methods – The problem here is that the clients cannot override the functionality if they would like to. This means clients will have to use the single implementation which does not really give them flexibility.

So how do we achieve all of the following?

  1. Add new functionality to an old library.
  2. Not break existing client code.
  3. Allow clients to inherit behavior and not state
  4. Allow clients to override behavior if needed

The answer to this is the default methods. A default method can be directly added to an interface with an implementation. Yes, you read that right! Interfaces with methods and not just the declaration but definition as well.

public interface ExistingInterface {
public void connect();
public void disconnect();
//Default method.
public default void connectUsingNewProtocol(){
System.out.println("Connection to a new device");
}
}

Some examples in the Java library which use the the default method:

1) stream method in the Collection interface – Enables any collection to be streamed.

    default Stream<E> stream() {
        return ...;
    }

2) reversed method in the Comparator interface – Enables reversing of the comparator

    default Comparator<T> reversed() {
        return Collections.reverseOrder(this);
    }

3) forEach and spliterator in the Iterable interface

One purpose of the default methods was to make changes to the Collection framework without breaking other libraries that use Collection API, like the Apache CollectionUtils or Hibernate and at the same time provide us with new functionality.

We all know that an interface can extend from another interface in Java.  By adding default methods, things get a little complicated and hence there are some things to remember when using default methods:

In the code below, Parent interface has default methods, Child extends from Parent but does not provide implementation.

interface Parent {
default public void methodOne() {
System.out.println("Method one…");
}
default public void methodTwo() {
System.out.println("Method two…");
}
}
interface Child extends Parent{
}
public class ConcreteClass implements Child {
public static void main(String[] args)
{
ConcreteClass concreteClass = new ConcreteClass();
concreteClass.methodOne();
concreteClass.methodTwo();
}
}
}

Output:

Method one…

Method two…

Conclusion : Default methods are inherited in the Child interface.

In the next example below :

Parent interface has default method, Child extends from Parent and does provide or override implementation.

interface Parent
{
default public void methodOne() {
System.out.println("Parent : Method one…");
}
default public void methodTwo() {
System.out.println("Parent : Method two…");
}
}
interface Child extends Parent
{
//overrides methodTwo
default public void methodTwo(){
System.out.println("Child : Method two…");
}
}
public class ConcreteClass implements Child
{
public static void main(String[] args)
{
ConcreteClass concreteClass = new ConcreteClass ();
concreteClass.methodOne();
concreteClass.methodTwo();
}
}

Output:

Parent: Method one…

Child: Method two…

Conclusion:The most specific/ the one at the bottom in the interface hierarchy wins!

What if the ConcreteClass class also provided the implementation for the methodTwo() above ?

public class ConcreteClass implements Child {
public void methodTwo(){
System.out.println("ConcreteClass : Method two…");
}
public static void main(String[] args) {
ConcreteClass concreteClass = new ConcreteClass ();
concreteClass.methodOne();
concreteClass.methodTwo();
}
}

Output:

Parent: Method one…

ConcreteClass: Method two…

Conclusion : Class wins over interface hierarchy!

What happens when we have same default methods in 2 “unrelated” interface and 1 concrete class implementing the 2 interfaces

interface One {
default public void callMe(){
System.out.println("One:callMe…");
}
}
interface Two
{
default public void callMe()
{
System.out.println("Two:callMe…");
}
}
public class ConcreteClass implements One, Two
{
public static void main(String[] args)
{
}
}

Output: The java compiler complains that the method callMe is being inherited from types One and Two. Well, it is our responsibility to resolve the same. This can be done in the following way:

public class ConcreteClass implements One, Two
{
@Override
public void callMe()
{
One.super.callMe();
}
public static void main(String[] args)
{
ConcreteClass concreteClass = new ConcreteClass();
concreteClass.callMe();
}
}

Depending on the implementation of the default method, we can either call the version One or the Two. This is done by using the super keyword. Without using the super keyword, it would mean we are calling the static method on the interface which is not the case here.  At the byte code level , this translates to a invokespecial instruction.

To summarize

  1. Interface hierarchy – the most specific wins
  2. Classes always win in case there is a class that implements a default method and there is an interface hierarchy.
  3. If there is still a problem, it is the responsibility of the developer to resolve it as shown above.

So, does this mean that we do not need abstract classes anymore? Well, that is not right.  What clearly distinguishes the two is state and the fact that a class can inherit only 1 class

So interfaces are always a better idea unless we need state or a skeletal implementation.

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 )

Google photo

You are commenting using your Google 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 )

Connecting to %s