Java - default methods in Interfaces

Reading material

https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html

What is the default method, and why is it required?

Before Java 8, interfaces could have only abstract methods. The implementation of these abstract methods has to be provided in a separate class. So, if a new method is to be added in an interface then its implementation code has to be provided in the class implementing the same interface.

To overcome this issue, Java 8 has introduced the concept of default methods. Default methods in interfaces allow the interfaces to have methods with implementation without affecting the classes that implement the interface.

The default methods were introduced for these reasons

  1. to provide backward comparability so that existing classes that are implementing these interfaces don’t need any changes when default methods are added to the interfaces.
  2. existing classes that are implementing the interfaces can use these default methods without having to write their own implementation.

Default methods are also known as defender methods or virtual extension methods.

Features

Default methods are methods that can have a body in interfaces.

Implementation examples

In Java language itself, the Comparator interface makes use of this heavily.

See

  1. https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html
  2. Comparator interface vs Comparable interface

Uses

  1. To provide additional functionality to a given type without breaking the implementing classes.

  2. Not break existing Impl classes when adding new methods to an existing interface.

    Before Java 8, if a new method was introduced in an interface then all the implementing classes used to break. We would need to provide the implementation of that method in all the implementing classes.

    However, sometimes methods have only single implementation and there is no need to provide their implementation in each class. In that case, we can declare that method as a default in the interface and provide its implementation in the interface itself.

    @FunctionalInterface // Annotation is optional
    public interface Vehicle {
    
        // The Abstract Method in the interface
        void cleanVehicle();
    
        // Default Method - Optional can be 0 or more
        default void startVehicle() {
         System.out.println("Vehicle is starting");
        }
    }
    

    A class that implements the vehicle interface looks like this. The class needs to implement only the abstract method cleanVehicle(). When we call the default method startVehicle(), the code defined in the interface is executed.

    public class Car implements Vehicle {
        @Override
        public void cleanVehicle() {
         System.out.println("Cleaning the vehicle");
        }
    
        public static void main(String args[]){
         Car car = new Car();
         car.cleanVehicle();
         car.startVehicle();
        }
    }
    
  3. If an interface is getting too big (with too many methods in it) but you want to split the Impl classes into smaller parts (based on some type of grouping).

    1. This is a common scenario if you are using Contract first approach to API development because the interface generated is based on the specification file.
    2. The interface can have many methods
    3. Each of the Impl classes can override one or only a specific set of the methods in the interface

java8 - default methods - solving the diamond problem with default methods in interfaces

What if a class implements multiple interfaces and the default methods in those interfaces have the same name?

InterfaceA

public interface InterfaceA {

    default void printSomething() {
        System.out.println("I am inside A interface");
    }
}

InterfaceB

public interface InterfaceB {

    default void printSomething() {
        System.out.println("I am inside B interface");
    }
}

There is a class that implements both these interfaces.

public class Main implements InterfaceA, InterfaceB {

}

Do we need to implement the printSomething() method in the Main class? Will the class compile if we don’t?

If some class calls the printSomething() method from the object of Main class then which implementation will be called? Will it call the method defined in interfaceA or interfaceB?

The above Main class will not compile because of the Diamond problem in Java. To resolve the compilation issue, we will have to implement the printSomething() method as shown below:

 public class Main implements InterfaceA, InterfaceB {

    @Override
    public void printSomething() {

        //Option 1 -> Provide our own implementation.
        System.out.println("I am inside Main class");

        //Option 2 -> Use existing implementation from interfaceA or interfaceB or both.
        InterfaceA.super.printSomething();
        InterfaceB.super.printSomething();
    }

    public static void main(String args[]){
         Main main = new Main();
         main.printSomething();
    }
}