Interfaces, Default Methods, and Method Resolution Order
Table of Contents
Interfaces in Java #
In Java, an interface is a reference type that can contain only constants, method signatures, default methods, static methods, and nested types. It is a blueprint of a class that defines a set of methods that the class must implement. An interface can be implemented by a class or extended by another interface.
Here is an example of an interface in Java:
public interface Shape {
double getArea();
double getPerimeter();
}
In this example, the Shape interface defines two methods: getArea() and getPerimeter(). Any class that implements the Shape interface must provide implementations for these methods.
Default Methods in Interfaces #
In Java 8, default methods were introduced in interfaces to provide backward compatibility and allow interfaces to have concrete methods. A default method is a method that has a body and is declared with the default keyword. Classes that implement the interface can choose to override the default method or use the default implementation provided by the interface.
Here is an example of a default method in an interface:
public interface Shape {
double getArea();
double getPerimeter();
default void printDescription() {
System.out.println("This is a shape with an area of " + getArea()
+ " and a perimeter of " + getPerimeter());
}
}
In this example, the Shape interface has a default method printDescription() that prints a description of the shape. Classes that implement the Shape interface can choose to override this method or use the default implementation.
Method Resolution Order (MRO) in Interfaces #
When a class implements multiple interfaces that have default methods with the same signature, a conflict arises. Java uses a set of rules to determine which default method implementation should be used. This process is known as Method Resolution Order (MRO).
The rules for Method Resolution Order in Java are as follows:
First consider the two interfaces A and B, that both have a default method foo():
public interface A {
default void foo() {
System.out.println("A");
}
}
public interface B extends A {
default void foo() {
System.out.println("B");
}
}
- Classes Win: If a class inherits a method from a superclass and also implements the same method from an interface, the class method takes precedence.
public class C extends D implements A, B {
public void foo() {
System.out.println("C");
}
public static void main(String[] args) {
new C().foo();
}
}
Output is C.
- Subinterfaces Win: If a class implements two interfaces that have default methods with the same signature, and one interface extends the other, the default method from the subinterface is preferred.
public interface A {
default void foo() {
System.out.println("A");
}
}
public interface B extends A {
default void foo() {
System.out.println("B");
}
}
public class C implements A, B {
public static void main(String[] args) {
new C().foo();
}
}
Output is B.
- No Inheritance: If the above rules do not resolve the conflict, the class implementing the interfaces must provide an implementation for the conflicting method.
public interface A {
default void foo() {
System.out.println("A");
}
}
public interface B {
default void foo() {
System.out.println("B");
}
}
public class C implements A, B {
public static void main(String[] args) {
new C().foo();
}
}
Output is an error because the class C does not provide an implementation for the conflicting method foo().
By following these rules, Java ensures that the method resolution order is unambiguous and that conflicts between default methods in interfaces can be resolved.