/ Backend Development

Default Interface method in Java

Creator Image
Bashar Alshaibani
26 May 2024 -
5 min Reading time

In Java, default methods in interfaces allow you to add new methods to interfaces without breaking the existing implementation of those interfaces. Here's a summary of the rules and behaviors associated with default methods:

Key Rules for Default Methods

  1. Default Method Declaration:

    • Default methods are declared using the default keyword in an interface.
    • Example:
      public interface MyInterface {
          default void myMethod() {
              // implementation
          }
      }
      
  2. Multiple Inheritance:

    • If a class implements multiple interfaces that provide a default method with the same signature, the compiler will report a conflict, and the implementing class must override the conflicting method to resolve the ambiguity.
  3. Inheritance Hierarchy:

    • If an interface extends another interface and both have a default method with the same signature, the method in the sub-interface overrides the one in the super-interface.
    • Example:
      interface A {
          default void foo() {
              System.out.println("A");
          }
      }
      
      interface B extends A {
          default void foo() {
              System.out.println("B");
          }
      }
      
      class C implements B {}
      
  4. Class vs. Interface:

    • If a class implements an interface with a default method and the class (or its superclass) provides a method with the same signature, the class method takes precedence over the interface's default method.
    • Example:
      interface A {
          default void foo() {
              System.out.println("A");
          }
      }
      
      class B {
          public void foo() {
              System.out.println("B");
          }
      }
      
      class C extends B implements A {}
      
  5. Abstract Class Implementing Interface:

    • An abstract class that implements an interface with a default method is not required to override the default method, but any concrete subclass must either inherit or override it.

Example Scenarios

Scenario 1: Interface Inheritance

interface Perishable1 {
    default int maxDays() {
        return 1;
    }
}

interface Perishable2 extends Perishable1 {
    default int maxDays() {
        return 2;
    }
}

class Milk implements Perishable2, Perishable1 {}

public class Test {
    public static void main(String[] args) {
        Perishable1 obj = new Milk();
        System.out.println(obj.maxDays()); // Outputs 2
    }
}
  • Perishable2 extends Perishable1 and overrides maxDays(). The Milk class uses the maxDays() method from Perishable2.

Scenario 2: Class Method Overriding Default Method

interface A {
    default void foo() {
        System.out.println("A");
    }
}

class B {
    public void foo() {
        System.out.println("B");
    }
}

class C extends B implements A {}

public class Test {
    public static void main(String[] args) {
        C obj = new C();
        obj.foo(); // Outputs "B"
    }
}
  • The class B has a method foo(), which overrides the default method foo() from interface A. When C extends B and implements A, the method from class B takes precedence.

Scenario 3: Multiple Interfaces with Conflicting Default Methods

interface A {
    default void foo() {
        System.out.println("A");
    }
}

interface B {
    default void foo() {
        System.out.println("B");
    }
}

class C implements A, B {
    @Override
    public void foo() {
        A.super.foo(); // or B.super.foo();
    }
}

public class Test {
    public static void main(String[] args) {
        C obj = new C();
        obj.foo(); // Outputs "A" or "B" depending on which super call is used
    }
}
  • The class C implements both A and B, which have conflicting foo() methods. C must override foo() to resolve the conflict and can use A.super.foo() or B.super.foo() to invoke the desired default method.

Summary

Understanding these rules helps manage default methods in interfaces and resolve conflicts that arise from multiple inheritance. These rules ensure that the behavior of methods is predictable and consistent, allowing interfaces to evolve without breaking existing implementations.