Introduction
Conflicts occur when a class implements multiple interfaces with the same default method signature.
The Problem
interface Interface1 {
default void display() {
System.out.println("Interface1");
}
}
interface Interface2 {
default void display() {
System.out.println("Interface2");
}
}
// ✗ Conflict! Which display() to use?
class MyClass implements Interface1, Interface2 {
// Compilation error without resolution
}
Error: Inherits unrelated defaults for display() from Interface1 and Interface2
Resolution Rule
Must override the conflicting method in implementing class.
interface Interface1 {
default void display() {
System.out.println("Interface1");
}
}
interface Interface2 {
default void display() {
System.out.println("Interface2");
}
}
class MyClass implements Interface1, Interface2 {
// ✓ Resolve by overriding
@Override
public void display() {
System.out.println("MyClass");
}
}
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.display(); // MyClass
}
}
Calling Specific Interface Method
Use InterfaceName.super.methodName() syntax.
interface Interface1 {
default void display() {
System.out.println("Interface1");
}
}
interface Interface2 {
default void display() {
System.out.println("Interface2");
}
}
class MyClass implements Interface1, Interface2 {
@Override
public void display() {
// Call Interface1's version
Interface1.super.display();
// Call Interface2's version
Interface2.super.display();
// Own logic
System.out.println("MyClass");
}
}
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.display();
// Output:
// Interface1
// Interface2
// MyClass
}
}
Choosing One Interface
interface Logger {
default void log(String message) {
System.out.println("[LOG] " + message);
}
}
interface Printer {
default void log(String message) {
System.out.println("[PRINT] " + message);
}
}
class MyLogger implements Logger, Printer {
@Override
public void log(String message) {
// Choose Logger's implementation
Logger.super.log(message);
}
}
public class Main {
public static void main(String[] args) {
MyLogger logger = new MyLogger();
logger.log("Test"); // [LOG] Test
}
}
Resolution Rules Summary
Priority Order:
- Class wins - class method overrides interface default
- Sub-interface wins - more specific interface overrides parent
- Manual resolution - if conflict, must override explicitly
Rule 1: Class Wins
Class method takes priority over interface default method.
interface MyInterface {
default void display() {
System.out.println("Interface");
}
}
class Parent {
public void display() {
System.out.println("Parent");
}
}
class Child extends Parent implements MyInterface {
// No conflict - Parent's method wins
}
public class Main {
public static void main(String[] args) {
Child obj = new Child();
obj.display(); // Parent (class wins)
}
}
Rule 2: Sub-interface Wins
More specific interface overrides parent interface.
interface Parent {
default void display() {
System.out.println("Parent Interface");
}
}
interface Child extends Parent {
@Override
default void display() {
System.out.println("Child Interface");
}
}
class MyClass implements Child {
// No conflict - Child interface wins
}
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.display(); // Child Interface
}
}
Rule 3: Manual Resolution
Unrelated interfaces with same method - must resolve manually.
interface A {
default void show() {
System.out.println("A");
}
}
interface B {
default void show() {
System.out.println("B");
}
}
class MyClass implements A, B {
// Must override to resolve conflict
@Override
public void show() {
A.super.show(); // Or B.super.show() or custom
}
}
Complete Example: Multiple Conflicts
interface Drawable {
default void draw() {
System.out.println("Drawing");
}
default void display() {
System.out.println("Drawable display");
}
}
interface Printable {
default void print() {
System.out.println("Printing");
}
default void display() {
System.out.println("Printable display");
}
}
class Document implements Drawable, Printable {
// Must resolve display() conflict
@Override
public void display() {
System.out.println("Document display");
// Can call both if needed
System.out.println("From Drawable:");
Drawable.super.display();
System.out.println("From Printable:");
Printable.super.display();
}
// draw() and print() work fine (no conflict)
}
public class Main {
public static void main(String[] args) {
Document doc = new Document();
doc.draw(); // Drawing
doc.print(); // Printing
doc.display(); // Custom resolution
}
}
Diamond Problem
interface A {
default void show() {
System.out.println("A");
}
}
interface B extends A { }
interface C extends A { }
// Diamond: A -> B, A -> C, MyClass implements B,C
class MyClass implements B, C {
// No conflict - both inherit same method from A
// No need to override
}
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.show(); // A (no ambiguity)
}
}
Diamond with Override
interface A {
default void show() {
System.out.println("A");
}
}
interface B extends A {
@Override
default void show() {
System.out.println("B");
}
}
interface C extends A {
@Override
default void show() {
System.out.println("C");
}
}
// Conflict! B and C both override A.show() differently
class MyClass implements B, C {
// Must resolve
@Override
public void show() {
B.super.show(); // Or C.super.show() or custom
}
}
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.show(); // B
}
}
Real-World Example: Logging System
interface FileLogger {
default void log(String message) {
System.out.println("[FILE] " + message);
}
default void logError(String error) {
log("ERROR: " + error);
}
}
interface DatabaseLogger {
default void log(String message) {
System.out.println("[DATABASE] " + message);
}
default void logWarning(String warning) {
log("WARNING: " + warning);
}
}
class HybridLogger implements FileLogger, DatabaseLogger {
// Resolve log() conflict
@Override
public void log(String message) {
// Log to both
FileLogger.super.log(message);
DatabaseLogger.super.log(message);
}
// logError() and logWarning() work fine
}
public class Main {
public static void main(String[] args) {
HybridLogger logger = new HybridLogger();
logger.log("User login");
// [FILE] User login
// [DATABASE] User login
logger.logError("Connection failed");
// [FILE] ERROR: Connection failed
// [DATABASE] ERROR: Connection failed
logger.logWarning("Low memory");
// [FILE] WARNING: Low memory
// [DATABASE] WARNING: Low memory
}
}
Multiple Inheritance Scenario
interface Movable {
default void move() {
System.out.println("Moving");
}
}
interface Flyable {
default void move() {
System.out.println("Flying");
}
}
interface Swimmable {
default void move() {
System.out.println("Swimming");
}
}
class Duck implements Movable, Flyable, Swimmable {
private String currentMode = "walking";
void setMode(String mode) {
this.currentMode = mode;
}
@Override
public void move() {
switch (currentMode) {
case "walking":
Movable.super.move();
break;
case "flying":
Flyable.super.move();
break;
case "swimming":
Swimmable.super.move();
break;
}
}
}
public class Main {
public static void main(String[] args) {
Duck duck = new Duck();
duck.setMode("walking");
duck.move(); // Moving
duck.setMode("flying");
duck.move(); // Flying
duck.setMode("swimming");
duck.move(); // Swimming
}
}
Abstract Method Wins
Abstract method forces implementation, overrides default.
interface A {
default void display() {
System.out.println("Default A");
}
}
interface B {
void display(); // Abstract
}
class MyClass implements A, B {
// Must implement because B has abstract display()
@Override
public void display() {
System.out.println("MyClass implementation");
// Can still call A's default if needed
// A.super.display();
}
}
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.display(); // MyClass implementation
}
}
Best Practices
- Document conflicts in interface JavaDoc
- Avoid same method names in unrelated interfaces
- Use explicit resolution for clarity
- Consider composition over multiple inheritance
- Test conflict scenarios thoroughly
Resolution Strategies
Strategy 1: Choose One
class MyClass implements A, B {
public void method() {
A.super.method(); // Use A's version
}
}
Strategy 2: Call Both
class MyClass implements A, B {
public void method() {
A.super.method();
B.super.method();
}
}
Strategy 3: Custom Logic
class MyClass implements A, B {
public void method() {
// Custom implementation
System.out.println("Custom");
}
}
Strategy 4: Combine
class MyClass implements A, B {
public void method() {
A.super.method();
System.out.println("Additional logic");
B.super.method();
}
}
Quick Reference
// Conflict scenario
interface I1 {
default void method() { }
}
interface I2 {
default void method() { }
}
// Must resolve
class MyClass implements I1, I2 {
@Override
public void method() {
// Choose one
I1.super.method();
// Or both
I1.super.method();
I2.super.method();
// Or custom
System.out.println("Custom");
}
}
// Priority:
// 1. Class method wins over interface default
// 2. Sub-interface wins over parent interface
// 3. Otherwise, must resolve manually
Common Scenarios
| Scenario | Resolution |
|---|---|
| Two unrelated interfaces | Must override |
| Sub-interface extends parent | Sub-interface wins |
| Class extends & implements | Class wins |
| Diamond (same default) | No conflict |
| Diamond (different defaults) | Must override |
| Abstract + default | Must implement |
Exam Tips
Remember:
- Conflict when same method in multiple interfaces
- Must override to resolve
- Syntax:
Interface.super.method() - Priority: Class > Sub-interface > Manual
- Abstract wins over default
- Diamond with same method = no conflict
- Diamond with different methods = conflict
- Can call multiple interface versions
- Custom logic allowed
- @Override annotation required
Common Questions:
- When does conflict occur?
- How to resolve conflict?
- What is Interface.super syntax?
- Priority rules for conflicts?
- Class method vs default method?
- Sub-interface vs parent interface?
- Diamond problem in interfaces?
- Abstract vs default in conflict?
- Can we call both interface methods?
- Is override mandatory for conflicts?