Composition vs Inheritance — has-a vs is-a
Fast recall
Quick revision
Core answer
extends; the child receives inherited accessible behavior and must satisfy the parent contract. Composition builds an object from collaborators held in fields and delegates work to them. Neither choice automatically creates tight or loose coupling, and composition is replaceable at runtime only when the design exposes that option.Interview-ready guide
Interview answer
Inheritance models a subtype. If Dog extends Animal, callers using the Animal contract should be able to use a Dog without special rules. Composition models collaboration: a Car contains an Engine and delegates engine work.
The practical difference is not simply 'inheritance is tight and composition is loose.' A carefully designed base class can be a stable extension point, while a composed class can still be tied to a large concrete collaborator. Composition gives a useful seam when the owner depends on a small interface and receives the collaborator from outside. For example, CheckoutService can receive a PaymentGateway; production and test code can provide different implementations. I use inheritance when the subtype contract is real and supported, and composition when the relationship is has-a or when I only need reusable behavior.
Composition vs Inheritance — has-a vs is-a
Direct Answer
Inheritance defines a subtype with extends; the child receives inherited accessible behavior and must satisfy the parent contract. Composition builds an object from collaborators held in fields and delegates work to them. Neither choice automatically creates tight or loose coupling, and composition is replaceable at runtime only when the design exposes that option.
Code Example
interface Engine {
void start();
}
final class PetrolEngine implements Engine {
public void start() { System.out.println("petrol"); }
}
final class Car {
private final Engine engine;
Car(Engine engine) {
this.engine = engine;
}
void start() {
engine.start();
}
}
Car car = new Car(new PetrolEngine());
Car uses composition and its engine is fixed after construction. A different Car can receive another Engine; this object does not magically swap collaborators at runtime.
Common Mistakes & Pitfalls
- Saying a subclass inherits everything: constructors are not inherited, and private members are not directly accessible.
- Calling every field loose coupling: a field typed as a large concrete class can still bind the owner closely to it.
- Promising runtime swapping: many composed dependencies are fixed after construction.
- Using inheritance only to borrow methods: delegation usually avoids a false subtype.
- Using a real-world noun test alone: behavioral substitutability matters more than whether the sentence 'X is a Y' sounds natural.
Check your understanding
Answer these without looking back at the article.
- When should you use inheritance?
- What is the fragile base class problem?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.