skip to content

Composition & Aggregation

Building behavior by holding other objects rather than extending them, and the HAS-A relationships that result. Interviewers use it to see whether you reach for inheritance reflexively.

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

questions

9

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

What does 'favor composition over inheritance' mean in Java, and why is it a recommended default?

level: juniorimportance: must knowfreq 78%

basics

~20 s

Instead 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.

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

How do you implement reuse via the delegation pattern in Java? Show held fields and forwarding methods.

level: middleimportance: must knowfreq 62%

basics

~10 s

Keep the other object in a private field, and write methods that simply call the matching method on that held object. Those one-line pass-through methods are the forwarding methods.

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

When is inheritance the right choice over composition in Java, and what tests help you decide?

level: seniorimportance: should knowfreq 55%

basics

~20 s

Use inheritance only when the child truly IS-A parent and can be used anywhere the parent is expected, and the parent was designed to be extended. Otherwise, compose by holding the other object as a field.

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

What are the trade-offs of composition-plus-delegation at scale, and how do modern Java features change the calculus?

level: principalimportance: nice to knowfreq 33%

basics

~20 s

Composition is safer and more flexible but adds forwarding boilerplate and an extra indirection. Interfaces with default methods, records, sealed types, and dependency injection reduce the cost and make composition the practical default in large systems.

open as a page