Favor composition over inheritance — why?
Fast recall
Quick revision
Core answer
Interview-ready guide
Interview answer
Composition is often preferred because it lets a class delegate through an explicit collaborator instead of inheriting a whole implementation. For example, OrderService can receive a Notifier; a test can provide a small fake and production can provide an email notifier. That keeps the service focused on the operations it needs.
I would still qualify the rule. A concrete composed dependency can be tightly coupled, and a well-designed abstract base class can be a sound extension point. Inheritance is appropriate when the child must be substitutable for the parent and the parent documents how subclasses extend it. The real guideline is: do not create a subtype merely to reuse code. Use the mechanism whose contract matches the relationship.
Favor composition over inheritance — why
Direct Answer
'Favor composition over inheritance' is a guideline for reuse, not a rule that composition is always better. Composition can keep dependencies explicit and limit exposure to base-class internals. It is most helpful when behavior is optional or replaceable. Inheritance remains suitable for a stable, documented subtype or framework extension point.
Code Example
interface Notifier {
void send(String message);
}
final class OrderService {
private final Notifier notifier;
OrderService(Notifier notifier) {
this.notifier = notifier;
}
void placeOrder() {
// Save the order, then notify the customer.
notifier.send("Order placed");
}
}
OrderService service = new OrderService(new EmailNotifier());
The collaborator is selected when OrderService is created. Production can provide EmailNotifier, while a focused test can provide a fake. This is a useful seam, not a promise that the same service object can hot-swap its notifier later.
Common Mistakes & Pitfalls
- Treating composition as automatically loose: the declared collaborator contract decides the coupling.
- Saying inherited code cannot be tested: inheritance may be tested directly or through a controlled subclass.
- Creating an interface only for a mock framework mock: a simple real collaborator or fake may be clearer.
- Rejecting every inheritance hierarchy: supported subtypes and framework extension points remain valid.
- Assuming dependencies must change at runtime: selecting an implementation at object creation is often enough.
Check your understanding
Answer these without looking back at the article.
- What is the fragile base class problem?
- Give an example where inheritance is the right choice
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.