Q18 of 40 · Core Java
What is the diamond problem in Java and how does Java handle it?
Short answer
Short answer: The diamond problem arises when a class inherits from two sources that both provide the same method, creating ambiguity about which implementation to use. Java avoids it with single class inheritance. For interfaces with default methods (Java 8+), if two interfaces provide the same default method, the implementing class must explicitly override it to resolve the conflict.
Detail
In languages with multiple class inheritance (C++), if class D extends both B and C, and both inherit from A, there's ambiguity when D.method() is called — which path through the diamond should run? This is the classic diamond problem.
Java's solution: a class can only extend one class. Diamond inheritance via class hierarchy is structurally impossible. A class can implement multiple interfaces, but before Java 8, interfaces had no method bodies — there was nothing to conflict.
Java 8+ default methods created a limited diamond: if two interfaces both declare a default method with the same signature, a class implementing both gets a compile error and must override the method to resolve the ambiguity. Java requires explicit resolution, unlike C++ where the resolution rules are implicit and error-prone.
Resolution rules (compiler-enforced priority):
- Class implementation always wins over interface default methods.
- More specific interface (subinterface) wins over less specific.
- If neither rule resolves it, the class must override and can call a specific interface's default via
InterfaceName.super.method().
In test automation, this comes up when building capability-based Page Object hierarchies — a page that implements both Searchable and Navigable might find both provide a default waitForReady() implementation. The compiler surfaces the conflict immediately rather than producing silent wrong behaviour.
// EXAMPLE
DiamondProblem.java
interface Searchable {
default String waitForReady() {
return "waiting for search to load";
}
}
interface Navigable {
default String waitForReady() {
return "waiting for navigation to load";
}
}
// ❌ Compile error: class inherits unrelated defaults for waitForReady()
// from both Searchable and Navigable
class ProductsPage implements Searchable, Navigable {
// Must override to resolve the conflict
@Override
public String waitForReady() {
// Option 1: pick one interface's implementation explicitly
return Searchable.super.waitForReady();
// Option 2: provide entirely new implementation
// return "waiting for products page";
}
}
// No conflict: class method always wins over default method
class AdminPage implements Searchable {
@Override
public String waitForReady() {
return "admin page ready"; // this class wins over Searchable's default
}
}