skip to content

Composition vs Aggregation

Composition means the owner controls the part's lifecycle, aggregation means the part is shared and outlives the whole. Interviewers ask you to classify a relationship and back it with the code that expresses it.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

What is the difference between a HAS-A and an IS-A relationship in Java, and how does each show up in code?

level: juniorimportance: must knowfreq 70%

answer

  1. IS-A = extends/implements; HAS-A = field
  2. IS-A must satisfy Liskov substitutability
  3. HAS-A = containment + delegation
  4. Favor composition (HAS-A) over inheritance (IS-A)
  5. Inheritance for reuse only = code smell

basics

~20 s

IS-A means one type is a kind of another (a Car IS-A Vehicle) and uses inheritance (extends). HAS-A means an object holds another as a part (a Car HAS-A Engine) and uses a field. IS-A is inheritance; HAS-A is composition.

solid answer

~40 s

IS-A is the inheritance relationship: a subclass is a specialized kind of its superclass, so Dog extends Animal means a Dog IS-AN Animal and can be used wherever an Animal is expected. In Java you express it with extends (for classes) or implements (for interfaces). HAS-A is the containment relationship: one object holds a reference to another as a member field, like a Car HAS-A Engine. You express HAS-A simply by declaring the other type as a field. The practical guidance is to use IS-A only when the subtype genuinely substitutes for the supertype (Liskov); otherwise model the relationship as HAS-A. Most real-world 'part of' or 'uses a' relationships are HAS-A, which is why composition is usually the safer default over inheritance.

code

java · 10 lines
java
// IS-A: SportsCar substitutes for Car
class Car { void drive() {} }
class SportsCar extends Car { void turbo() {} }

// HAS-A: Car holds an Engine as a part
class Engine { void start() {} }
class Car2 {
    private final Engine engine = new Engine(); // HAS-A
    void start() { engine.start(); }            // delegation
}

go deeper

for a junior

Can state IS-A = extends/implements, HAS-A = a field, and give one example of each (Dog IS-A Animal; Car HAS-A Engine).

for a middle

Explains delegation as the HAS-A mechanism and recognizes that inheritance forces substitutability, so HAS-A is the default for 'uses/part-of' relationships.

for a senior

Ties IS-A to the Liskov Substitution Principle, names the fragile-base-class coupling that makes inheritance risky, and applies 'favor composition over inheritance' as a deliberate design choice.

for a principal

Frames the choice in terms of API surface, long-term substitutability guarantees, and evolution cost; can articulate when an inheritance hierarchy is still the right call (stable IS-A, framework template methods) versus when composition keeps the design flexible.

## The two ways objects relate When you design with objects, two objects can relate in two fundamentally different ways. Naming them with plain-English phrases makes the distinction obvious. ### IS-A (inheritance) **IS-A** means one type *is a kind of* another. A `Dog` IS-AN `Animal`; a `SavingsAccount` IS-AN `Account`. In Java you create an IS-A relationship with the **`extends`** keyword (class inherits from a class) or **`implements`** (class promises an interface's behavior). Inheritance means the subclass automatically gets the superclass's fields and methods, and — crucially — an instance of the subclass can be used **anywhere** the superclass type is expected. That substitutability is the whole point of IS-A. The formal rule is the **Liskov Substitution Principle (LSP)**: if `S` IS-A `T`, then you must be able to replace a `T` with an `S` without breaking the program. ```java class Animal { void breathe() {} } class Dog extends Animal { void bark() {} } // Dog IS-A Animal Animal a = new Dog(); // legal: a Dog substitutes for an Animal ``` ### HAS-A (composition / association) **HAS-A** means one object *holds* or *contains* another as a part. A `Car` HAS-AN `Engine`; an `Order` HAS-A list of `LineItem`s. In Java you express HAS-A by declaring the other type as a **member field** (a variable inside the class): ```java class Engine { void start() {} } class Car { private final Engine engine = new Engine(); // Car HAS-A Engine void start() { engine.start(); } // the Car delegates to its part } ``` Here `Car` does not *become* an `Engine`; it simply *uses* one. Calling the Car's method that forwards work to the held object is called **delegation**. ### Why the distinction matters Inheritance is the strongest coupling Java offers: the subclass depends on the superclass's internal details and breaks if the parent changes (the 'fragile base class' problem). It also forces an IS-A claim that must hold *forever*. HAS-A is looser: the container only depends on the contained type's public methods and can swap implementations freely. The classic design guidance — **'favor composition over inheritance'** (from *Effective Java* and *Design Patterns*) — says: reach for HAS-A by default, and use IS-A only when a true, permanent substitutability relationship exists. ### A quick test Ask: 'Can I sensibly say *X is a Y*, and could I pass an X to every method that wants a Y?' If yes, IS-A may fit. If you instead find yourself saying *X has a Y* or *X uses a Y*, it's HAS-A — model it as a field. A common mistake is using inheritance just to reuse code (e.g., `Stack extends Vector`), which leaks the parent's API and violates IS-A; a field plus delegation would have been cleaner.

  • Why is 'using inheritance only to reuse code' considered a design smell?
    Inheritance forces an IS-A claim and exposes the parent's whole API, coupling the subclass to the parent's internals (fragile base class). If you only wanted to reuse the code, a field plus delegation (HAS-A) reuses behavior without the substitutability promise or the leaked API.
  • Can a class have both an IS-A and a HAS-A relationship at once?
    Yes. A class commonly extends one superclass (IS-A) while also holding other objects as fields (HAS-A). For example, a SportsCar extends Car (IS-A) and still has an Engine field (HAS-A).

IS-A is 'a sparrow is a bird' — it belongs to the category. HAS-A is 'a bird has a wing' — the wing is a part, not the category.

saying these in an interview costs you the question

  • Saying HAS-A uses 'extends' — HAS-A is a field, not inheritance.
  • Claiming any code reuse justifies inheritance — reuse alone is not an IS-A relationship.
  • Confusing 'implements an interface' with HAS-A — implementing is still IS-A (multiple inheritance of type).

context

open as a page

Within HAS-A relationships, how do composition and aggregation differ in terms of ownership and lifecycle?

level: middleimportance: must knowfreq 65%

basics

~20 s

Both are HAS-A. Composition is strong ownership: the part can't live without the whole, and when the whole is destroyed, so is the part (a House HAS rooms). Aggregation is weak: the part can exist independently and may be shared (a Team HAS players who outlive the team).

open as a page

When you compose a mutable object inside a class, what goes wrong without defensive copying, and how do you fix it?

level: seniorimportance: should knowfreq 55%

basics

~20 s

If you store a caller's mutable object directly, the caller still holds a reference and can change your object's internals behind your back. Fix it by copying the object when it comes in (constructor/setter) and when it goes out (getter), so outside code can't reach the part you own.

open as a page

How does delegation let you reuse a class's behavior via composition instead of inheritance, and what does a forwarding wrapper look like?

level: seniorimportance: should knowfreq 50%

basics

~20 s

Delegation means your class holds another object as a field and forwards calls to it instead of extending it. You reuse the behavior without inheriting the parent's whole API or being tied to its internals. A forwarding class implements an interface and passes each method through to the held instance.

open as a page

How would you decide between composition and inheritance when designing a class, and when is inheritance still the right choice?

level: principalimportance: should knowfreq 60%

basics

~20 s

Use inheritance only when there's a true, lasting IS-A relationship and the subtype can stand in for the supertype everywhere. Otherwise prefer composition: hold the other object as a field and forward to it. Inheritance is the strongest coupling, so make it a deliberate, narrow choice.

open as a page