🔍 Why Were Default Methods Introduced?
Before Java 8, adding a new method to an interface would break all existing classes that implemented the interface but didn’t provide the new method’s implementation. This led to widespread compile-time errors and forced developers to modify all implementing classes, which was especially painful when:
- The interface was widely used
- The interface was implemented in external libraries
To solve this, Java 8 introduced default methods, which allow an interface to provide a default implementation for a method. This means:
➤ New methods can be added without breaking existing implementations.
➤ Classes can still override the method if needed.
💡 Example 1 – Interface and Implementation Classes
Assume there’s a Notifier interface used to send notifications, and it has three implementations: EmailNotifier, SMSNotifier, and AppPushNotifier. Each one implements the notify() method.
public interface Notifier {
void notify(String message);
}
public class EmailNotifier implements Notifier {
@Override
public void notify(String message) {
System.out.println("[EMAIL] " + message);
}
}
(Same structure for SMSNotifier and AppPushNotifier.)
Everything works fine—until a new requirement comes in...
⚠️ Example 2 – Adding a New Method to Interface
We need to support scheduled notifications, so we add a method to the interface:
void scheduleNotification(String message, LocalDateTime scheduleTime);
Now, all implementing classes must override this new method, or a compile error occurs.
This breaks:
- Internal classes
- Classes from third-party libraries
- Any code that implements Notifier but hasn’t updated yet
This is a backward compatibility problem.
✅ Example 3 – Fixing with Default Method
We fix it by providing a default implementation for the new method:
default void scheduleNotification(String message, LocalDateTime scheduleTime) {
System.out.println("[Default Scheduling] message: " + message + ", time: " + scheduleTime);
}
Now:
- EmailNotifier can override it with custom behavior
- SMSNotifier, AppPushNotifier can skip implementing it and use the default
📘 What Are Default Methods?
Default methods allow method definitions (bodies) inside interfaces, which were previously not allowed.
Purpose:
- Define shared logic in an interface
- Avoid breaking existing implementations
Syntax:
public interface MyInterface {
void existingMethod();
default void newMethod() {
System.out.println("This is a new default method.");
}
}
Before Java 8:
- All methods in interfaces were abstract
- Only method signatures, no implementations allowed
🧠 Benefits of Default Methods
✅ Backward Compatibility
- Allows new functionality in interfaces without breaking existing code
✅ Extending Standard Libraries
- Java's standard interfaces (List, Collection, etc.) can now evolve
✅ Lambda & Stream API Support
- Enables interfaces to offer methods like stream(), forEach(), etc.
⚠️ Best Practices and Caveats
1. Use Only When Necessary
- Use default methods mainly to preserve backward compatibility
- Avoid overusing them just for convenience
2. Interfaces Should Stay Abstract
- Interface = contract
- Avoid putting complex logic in interfaces
3. Multiple Inheritance Conflict
- If two interfaces have the same default method signature, the implementing class must resolve the conflict:
interface A {
default void hello() { System.out.println("Hello from A"); }
}
interface B {
default void hello() { System.out.println("Hello from B"); }
}
public class MyClass implements A, B {
@Override
public void hello() {
// Conflict resolution here
A.super.hello(); // or B.super.hello();
}
}
4. Don’t Maintain State
- Default methods should not maintain internal state
- If state is needed, use a class instead
📝 Summary
Feature | Description |
Introduced | Java 8 |
Purpose | Add new interface methods without breaking old code |
Syntax | default keyword with method body |
Override? | Optional – if not overridden, default is used |
Good For | Backward compatibility, common simple logic |
public interface MyInterface {
void existingMethod();
default void newMethod() {
System.out.println("New default method");
}
}
🚨 Final Tips
- ✅ Use for library evolution
- ✅ Provide simple default behavior
- ❌ Avoid complex logic or shared state
- ❌ Don’t overuse – keep interfaces clean
'JAVA' 카테고리의 다른 글
Exception (0) | 2025.04.16 |
---|---|
Java Parallel Stream and Fork/Join Pattern (0) | 2025.04.04 |
Java Optional (0) | 2025.04.04 |
Java Method Reference (0) | 2025.04.02 |
Java Functional Interfaces (0) | 2025.04.01 |