Traps

93. Interface Default Method Diamond Problem

When a class inherits two interfaces with conflicting default methods, it must override to resolve the ambiguity โ€” the compiler rejects the ambiguity; a superclass method always wins over an interface default.

Java 8+ (default methods); Java 21 (core language)

Explanation

Java's resolution rules for default method conflicts:

Rule 1 โ€” Class always wins: if a class (or its superclass) provides a concrete method with the same signature, it takes precedence over any interface default. This ensures backwards compatibility โ€” pre-Java-8 classes are never affected by new default methods added to interfaces they implement.

Rule 2 โ€” More specific interface wins: if one interface extends the other, the more derived interface's default wins.

Rule 3 โ€” Must override: if Rules 1 and 2 don't resolve the conflict (two unrelated interfaces both provide a default with the same signature), the implementing class must explicitly override the method and choose which interface's implementation to invoke via InterfaceName.super.method().

Example

public class DefaultMethodDiamond {
    interface A {
        default String hello() { return "Hello from A"; }
    }

    interface B {
        default String hello() { return "Hello from B"; }
    }

    interface C extends A {
        @Override
        default String hello() { return "Hello from C (extends A)"; }
    }

    // Rule 1: class method wins over any default
    static class SuperClass {
        public String hello() { return "Hello from SuperClass"; }
    }

    // Rule 2: C (more specific) wins over A
    static class ImplAC implements A, C {
        // No override needed โ€” C wins (C extends A)
    }

    // Rule 3: A and B conflict โ€” must override
    static class ImplAB implements A, B {
        @Override
        public String hello() {
            // Choose explicitly, or provide own implementation
            return A.super.hello() + " + " + B.super.hello();
        }
    }

    // Rule 1: SuperClass.hello() wins over A.hello() and B.hello()
    static class ImplExtends extends SuperClass implements A, B {
        // No override needed โ€” SuperClass.hello() wins
    }

    public static void main(String[] args) {
        System.out.println(new ImplAC().hello());       // Hello from C (extends A)
        System.out.println(new ImplAB().hello());       // Hello from A + Hello from B
        System.out.println(new ImplExtends().hello());  // Hello from SuperClass

        // What happens without resolution: COMPILE ERROR
        // class Bad implements A, B {}  // error: inherits unrelated defaults
    }
}

Sources