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
- 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.
- 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
- https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html
- Comparator interface vs Comparable interface
Uses
-
To provide additional functionality to a given type without breaking the implementing classes.
-
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 methodstartVehicle()
, 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(); } }
-
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).
- 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.
- The interface can have many methods
- 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();
}
}