Default Methods vs Abstract Class in Java 8
Fast recall
Quick revision
Core answer
Whether you understand that Java's single inheritance limitation still applies and how default methods changed but didn't eliminate the interface-vs-abstract-class trade-off in Java 8.
Thinking default methods made abstract classes obsolete—abstract classes still provide constructors, instance fields, and protected access that interfaces cannot.
Explain the resolution order: a concrete class method wins, then the most-specific interface; an explicit override is needed only for unresolved defaults inherited from unrelated interfaces.
Interview-ready guide
Interview answer
Choose an interface with default methods when several unrelated classes share a contract and stateless reusable behavior. Choose an abstract class when related subclasses need common instance state, constructor rules, or protected implementation details. A class can implement several interfaces but extend only one class. Default methods also help evolve interfaces, which is why Java 8 added methods such as Collection.stream(). They do not replace abstract classes. If defaults overlap, remember the actual resolution order: a concrete class method wins, then a more-specific interface wins; only unresolved defaults from unrelated interfaces force an override.
Default Methods vs Abstract Class in Java 8
Direct Answer
Choose an interface with default methods when you want to define a contract that multiple unrelated classes can implement (since Java supports multiple interface implementation). Use an abstract class when you need to share state (fields), define constructors, or provide protected methods. Default methods added behavior to interfaces but didn't eliminate the need for abstract classes when you need true inheritance with shared state.
Code Example
This pairs a default method on an interface with a stateful abstract class, so the split is visible side by side:
public class DefaultVsAbstract {
// Interface + default method: shared behavior, no state, multiple inheritance
interface Greeter {
String name();
default String greet() { return "Hi, " + name(); } // default body
}
// Abstract class: shared behavior + instance state + constructor
abstract static class Account {
protected double balance; // instance state
Account(double opening) { balance = opening; }
void deposit(double a) { balance += a; }
abstract String type(); // subclass contract
}
record Person(String name) implements Greeter {}
public static void main(String[] args) {
System.out.println(new Person("Sam").greet()); // Hi, Sam
}
}
Greeter shares behavior with no state, so a record can implement it freely. Account needs a balance field and a constructor to seed it, which only an abstract class can provide.
Common Mistakes & Pitfalls
- Assuming default methods replaced abstract classes: abstract classes still provide constructors, instance fields, and protected members.
- Overstating the diamond conflict: a concrete class method wins over an interface default, and a more-specific subinterface default wins over its ancestor. A class must override only when unrelated inherited defaults remain unresolved; it may then call an eligible direct superinterface default with
InterfaceName.super.method(). - Forcing shared state into an interface: if related subclasses need common instance state and controlled initialization, an abstract class is often the direct model.
Check your understanding
Answer these without looking back at the article.
- What is the diamond problem with default methods?
- Can an abstract class implement an interface?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.