When should you prefer composition over inheritance in Java, and how do you implement it?
The deciding question is whether the relationship really is is-a. If a Car genuinely is a kind of Vehicle, inheritance models it well. If a Car merely has an Engine, composition is the honest model — and it is almost always the more flexible one.
Composition means holding a reference to the other object as a field and delegating to it. The class keeps its own identity and the collaborator's type can change at run time, which inheritance can never offer because a superclass is fixed at compile time.
public class Main {
public static void main(String[] args) {
// Composition: the vehicle HAS-A engine.
Vehicle petrol = new Vehicle(new PetrolEngine());
petrol.start();
System.out.println("engine : " + petrol.getEngine().getClass().getSimpleName());
// The same class, a different engine - impossible with inheritance.
Vehicle electric = new Vehicle(new ElectricEngine());
electric.start();
System.out.println("engine : " + electric.getEngine().getClass().getSimpleName());
}
}
class Engine {
void start() {
System.out.println("engine started");
}
}
class PetrolEngine extends Engine {
@Override
void start() {
System.out.println("petrol engine started");
}
}
class ElectricEngine extends Engine {
@Override
void start() {
System.out.println("electric engine started, silently");
}
}
class Vehicle {
private final Engine engine;
Vehicle(Engine engine) {
this.engine = engine;
}
void start() {
engine.start();
}
Engine getEngine() {
return engine;
}
}The output is the case for composition in one screen. The Vehicle class is identical in both cases; only the collaborator differs. With inheritance you would need a PetrolVehicle subclass and an ElectricVehicle subclass, and any existing code that held a Vehicle reference would be tied to one of them forever.
Composition also avoids the two classic inheritance problems. It does not create a false is-a relationship that lets a caller treat the composed object as the thing it merely contains. And it keeps the coupling explicit — the constructor says exactly what the class needs, so a test can supply a fake engine without any framework.