🔌 Interfaces & Abstraction · Intermediate

Diamond conflicts in Java

Two defaults with the same signature must be resolved with X.super.m().

🧩 The mysteryTwo interfaces, two default methods with the same name, one class in the middle. Java has to pick one… or does it refuse?
🔮 Predict it

Cat or dog?

Both interfaces bring a default sound(). What happens?

interface Cat {
    default String sound() { return "meow"; }
}
interface Dog {
    default String sound() { return "woof"; }
}
class CatDog implements Cat, Dog { }
void main() {
    System.out.println(new CatDog().sound());
}
  1. meow
  2. woof
  3. Compile error
Show the answer

Compile error. Neither default is more specific than the other, so any choice would be arbitrary. Java refuses to guess and forces CatDog to decide.

You break the tie

Override the method in your class. Inside, pick a version with **InterfaceName.super.method()** — or combine several. Calling Dog.super.sound() + Cat.super.sound() returns woofmeow, in exactly the order you write them.

class CatDog implements Cat, Dog {
    public String sound() {
        return Cat.super.sound();
    }
}
⚠️ The trap

Plain super is the wrong door

super.sound() does NOT mean "one of my interfaces". Plain super means the superclass — here Object — which has no sound(). To reach an interface's default, name it: Cat.super.sound().

public String sound() {
    return super.sound();  // error
}

Rule 1: the class wins

No conflict arises when a superclass already provides the method: a method inherited from a class always beats an interface default. And when one interface extends another and overrides its default, the more specific (sub-)interface wins.

class Base {
    public String name() { return "class"; }
}
class Kid extends Base implements Named { }
// Kid's name() comes from Base
🔮 Predict it

Class versus interface

Lamp offers a default, Light (a class) has a real method. What prints?

interface Lamp {
    default String color() { return "blue"; }
}
class Light {
    public String color() { return "red"; }
}
class Neon extends Light implements Lamp { }
void main() {
    System.out.println(new Neon().color());
}
  1. red
  2. blue
  3. Compile error
Show the answer

Class wins. Neon inherits color() from its superclass Light, which beats Lamp's default. No conflict is reported.

💼 In the real world

When upgrades collide

This bites real projects: you implement interfaces from two libraries, and after an upgrade both ship a default method with the same signature. Your code suddenly stops compiling. The fix takes three lines: override the method and delegate with X.super.m().

Key takeaways

  1. Two inherited defaults with the same signature → compile error until you override
  2. Pick a version with A.super.m(), or combine several
  3. 'Class wins': a method from a superclass beats any interface default
  4. A sub-interface's default beats the one it overrides
🤯 Did you know?

The "diamond problem" is named after the diamond shape of its inheritance diagram. Because interfaces carry no instance fields, Java's diamonds can only clash over methods — never over state.

Practice questions

What does this print?

interface A { default String hi() { return "A"; } }
interface B { default String hi() { return "B"; } }
class C implements A, B { }
void main() {
    System.out.println(new C().hi());
}
  1. A
  2. B
  3. Compile error
  4. AB
Check your answer

Compile error. C inherits two unrelated defaults with the same signature, so the compiler reports a conflict until C overrides hi().

Inside C.hi(), you want to call interface A's default version. What fills the blank?

class C implements A, B {
    public String hi() {
        return ___.hi();
    }
}
  1. A.super
  2. super.A
  3. A.this
  4. super
Check your answer

A.super. `A.super.hi()` invokes the default from the directly implemented interface A. Plain super would mean the superclass (Object).

Next: List.of(1, 2, 3) calls a method that lives *inside an interface* and isn't a default. So what is it?