What does 'favor composition over inheritance' mean in Java, and why is it a recommended default?
answer
- HAS-A (compose) vs IS-A (inherit)
- Fragile base class / HashSet addAll counted twice
- Delegation = held field + forwarding methods
- Inheritance breaks encapsulation across the class boundary
- Inherit only for true Liskov-substitutable IS-A
basics
~20 sInstead of one class extending another to reuse code, the class holds another object as a field and calls its methods. It is preferred because it couples classes less tightly and is more flexible to change.
solid answer
~40 s'Favor composition over inheritance' is a design guideline that says: when you want to reuse behavior, prefer holding another object as a field (HAS-A) and calling its methods, rather than subclassing it (IS-A). Inheritance ties a subclass to its superclass's internal implementation, so superclass changes can silently break subclasses — Effective Java calls this fragile-base-class problem. Inheritance is also static (fixed at compile time), exposes the parent's whole API, and Java only allows extending one class. Composition keeps each class's internals private, lets you swap the held object at runtime, expose only the methods you want, and combine many collaborators. Use inheritance only for a genuine IS-A relationship where the subclass is substitutable for the supertype; otherwise compose. The mechanical technique is delegation: the wrapper forwards selected calls to the held instance.
go deeper
Can state HAS-A (field) vs IS-A (extends) and that composition is the safer default for reuse because it couples classes less.
Explains the fragile-base-class problem with a concrete example and implements a delegating wrapper with private field plus forwarding methods.
Articulates the encapsulation/coupling trade-offs, ties the rule to LSP and the Effective Java guidance, and knows when inheritance (true IS-A, designed-for-extension) is correct.
Frames it as an API-design and evolution decision (long-term maintainability, package boundaries, designing-for-extension contracts) and weighs delegation boilerplate vs interface/default-method strategies at scale.
## The core idea **Code reuse** means using existing behavior without rewriting it. Object-oriented languages like Java give you two main ways to reuse a class's behavior in another class: 1. **Inheritance** — write `class Sub extends Super`. `Sub` automatically gains `Super`'s non-private methods and fields and *is a* kind of `Super`. This is the **IS-A** relationship. 2. **Composition** — give your class a field whose type is the other class, e.g. `private final Engine engine;`. Your class *has an* `Engine`. This is the **HAS-A** relationship. "**Favor composition over inheritance**" is a long-standing design guideline (popularized by the *Gang of Four* design-patterns book and by Joshua Bloch's *Effective Java*) that says: **when your goal is simply to reuse behavior, default to composition; reach for inheritance only when there is a true IS-A relationship.** ## Why inheritance is risky for reuse Inheritance creates the *tightest* coupling Java offers between two classes. Concretely: - **It violates encapsulation across the class boundary.** A subclass depends on *implementation details* of its superclass, not just its public contract. The classic *Effective Java* example: you extend `HashSet` to count how many elements were ever added, overriding both `add` and `addAll`. But `HashSet.addAll` internally calls `add`, so each element added via `addAll` is counted **twice**. The bug exists only because the subclass assumed something about how the superclass is implemented internally — and that internal detail can change between JDK versions. This is the **fragile base class problem**: a change to the superclass can silently break subclasses. - **It is decided at compile time and is permanent.** A `Sub` object is forever a `Super`; you cannot change that at runtime. - **It leaks the whole parent API.** Every public method of `Super` becomes part of `Sub`'s API whether or not it makes sense — e.g. a `Stack` that extends `Vector` accidentally exposes `add(index, element)`, letting callers insert in the middle and break stack semantics. - **Java allows only single implementation inheritance.** You can `extends` exactly one class, so inheritance "spends" your one slot. ## How composition fixes this — the delegation pattern The implementation technique behind composition-for-reuse is **delegation**: your class (the *wrapper* or *forwarding class*) holds an instance of the other class (the *delegate*) in a private field and implements its own methods by **forwarding** the call to that held instance. ```java public final class Car { private final Engine engine; // HAS-A Engine (held field) public Car(Engine engine) { this.engine = engine; } public void start() { engine.ignite(); } // forwarding method } ``` Because the `engine` field is private, `Car` exposes only `start()`, not the whole `Engine` API. `Car` does **not** become an `Engine`, so changes inside `Engine`'s implementation can only affect `Car` through `Engine`'s public contract — encapsulation is preserved. You can pass a different `Engine` (e.g. an `ElectricEngine`) into the constructor, so the collaborator is swappable at runtime. And `Car` is free to extend some *other* class if needed, since its single inheritance slot is unused. ## When inheritance is still right Inheritance is appropriate when: - There is a genuine **IS-A** relationship and the subtype is **Liskov-substitutable** (anywhere a `Super` is expected, a `Sub` works correctly), and - The superclass was *designed and documented for extension* (its self-use of overridable methods is documented), or it is your own class under your control. Extending interfaces ("inheritance of *type*") and using a class's own well-documented abstract skeleton (e.g. `AbstractList`) are fine. The warning is specifically about extending *concrete* classes across package/ownership boundaries purely to grab their code. ## Terms recap - **IS-A**: a subtype relationship; a `Cat` IS-A `Animal`. - **HAS-A**: an ownership/usage relationship; a `Car` HAS-A `Engine`. - **Delegation**: a method does its job by calling the same (or related) method on a held object. - **Forwarding method**: a one-line method that just passes the call to the delegate. - **Fragile base class problem**: subclasses breaking when an unrelated-looking change is made to a superclass. - **Liskov Substitution Principle (LSP)**: a subtype must be usable anywhere its supertype is expected without surprising behavior.
- Give a concrete example where inheritance silently breaks but composition would not.Extending HashSet to count additions: overriding add and addAll double-counts elements added via addAll, because HashSet.addAll internally calls add. A composition wrapper that holds a Set and forwards add/addAll counts each element exactly once and never depends on the Set's internal self-use.
- What is the main disadvantage of composition?Boilerplate: you must write forwarding methods for every delegated operation. For wide interfaces this is tedious, though IDEs can generate them and a reusable forwarding/abstract base class can absorb most of it.
saying these in an interview costs you the question
- Saying inheritance is always bad — it is correct for genuine IS-A, substitutable relationships and for inheriting type via interfaces.
- Claiming composition has no downside — it requires writing forwarding methods (boilerplate).
- Confusing composition-over-inheritance with composition-vs-aggregation (lifecycle ownership); they are different topics.
- Thinking the rule is about runtime performance; it is about coupling, encapsulation, and maintainability.