Default Methods
Default methods were introduced in Java 8 to allow interfaces to have method implementations. They enable interface evolution without breaking existing implementations and are a key enabler for functional programming features (lambdas, streams). This is a frequent interview topic, especially around diamond problem resolution.
What Is a Default Method?
A default method is a method in an interface that has a method body and is declared using the default keyword.
interface Vehicle {
default void start() {
System.out.println("Vehicle started");
}
}
- ✔ Method has implementation
- ✔ Classes implementing the interface inherit it automatically
Why Default Methods Were Introduced
Before Java 8:
- Interfaces could have only abstract methods
- Adding a new method broke all implementing classes
With Java 8:
- Default methods allow backward compatibility
- Existing classes need no change
Syntax Rules
interface InterfaceName {
default returnType methodName() {
// implementation
}
}
- ✔ Must have a body
- ✔ Cannot be abstract
- ✔ Implicitly public
Using Default Methods
Interface
interface Printer {
default void print() {
System.out.println("Printing...");
}
}
Implementing Class
class LaserPrinter implements Printer {
// no need to override print()
}
✔ print() available automatically
Overriding Default Methods
Implementing classes can override default methods.
class InkjetPrinter implements Printer {
@Override
public void print() {
System.out.println("Inkjet printing...");
}
}
✔ Class version takes precedence
Default Methods vs Abstract Methods
| Aspect | Default Method | Abstract Method |
|---|---|---|
| Has body | ✔ Yes | ❌ No |
| Mandatory override | ❌ No | ✔ Yes |
| Introduced | Java 8 | Java 1.0 |
| Backward compatible | ✔ Yes | ❌ No |
Default Methods vs Static Methods in Interface
| Aspect | Default Method | Static Method |
|---|---|---|
| Called via | Object | Interface name |
| Inherited | ✔ Yes | ❌ No |
| Overridable | ✔ Yes | ❌ No |
interface Utils {
default void show() {}
static void help() {}
}
Diamond Problem & Default Methods (Very Important)
Scenario: Two Interfaces, Same Default Method
interface A {
default void show() {
System.out.println("A");
}
}
interface B {
default void show() {
System.out.println("B");
}
}
class Test implements A, B {
// ❌ Compilation error unless overridden
}
Resolving the Conflict
class Test implements A, B {
@Override
public void show() {
A.super.show(); // or B.super.show()
}
}
- ✔ Class must override
- ✔ Explicitly choose which interface method to call
Interface vs Class Priority Rule (Interview Rule)
- Class methods win over interface default methods
- More specific interface wins
- If ambiguity → class must override
class Parent {
public void show() {}
}
interface Child {
default void show() {}
}
✔ Parent.show() is used
Default Methods & Functional Interfaces
- ✔ Functional interfaces can have default methods
- ✔ Only one abstract method allowed
@FunctionalInterface
interface Task {
void execute();
default void log() {
System.out.println("Logging...");
}
}
✔ Still a functional interface
Real-World Usage Examples
- Collection → forEach()
- List → sort()
- Iterator → forEachRemaining()
These were added without breaking old code.
Common Beginner Mistakes
- Forgetting default keyword
- Assuming default methods are mandatory
- Confusing static and default methods
- Not resolving diamond conflict
- Overusing default methods
Best Practices
- Use default methods sparingly
- Avoid adding business logic to interfaces
- Prefer abstract classes for stateful behavior
- Use default methods for common behavior
- Always resolve ambiguity explicitly
Interview-Ready Answers
Short Answer
Default methods allow interfaces to have method implementations.
Detailed Answer
In Java 8, default methods were introduced to allow interfaces to provide method implementations using the default keyword. They enable backward compatibility when interfaces evolve and support functional programming features, while still allowing implementing classes to override the default behavior.
Key Takeaway
Default methods make interfaces evolvable. They balance flexibility and compatibility, but should be used carefully to avoid design complexity.