Delete a WhatsApp group chat from your phone and every message inside it disappears with it. The messages were never stored anywhere else, nobody else was holding on to them, and a message without its chat has no meaning anyway. That is the kind of relationship this article is about. In object-oriented design it is called composition, and it is the strongest of the "has-a" relationships.

Composition means one object (the whole) creates its parts, owns them completely and decides when they live and die. The parts are not shared, and they do not exist outside the whole.

Real-world analogy: think of a printed book and its pages. The printer makes the pages as part of the book. You cannot lend page 47 to another book, and when the book is shredded the pages go with it. Compare that with a library shelf: remove the shelf and the books are still fine, because the shelf only held them. The shelf aggregates books. The book is composed of pages.

1. What Is Composition?

Composition is a special case of association with one extra rule: ownership. The whole is responsible for the full life of its parts. In practice that shows up in a few ways you can actually check in code.

  • The whole creates the part itself, usually inside a constructor or an add...() method. Callers pass plain data, not ready-made part objects.
  • The part belongs to exactly one whole. It is never handed to another object to keep.
  • When the whole goes away, the parts go away too.
  • The part has no real purpose on its own. A chat message without a chat, an order line without an order, a question without a question paper.

A quick test that works most of the time: if deleting the whole should also delete the part, you are looking at composition.

2. How Composition Looks in UML

In a class diagram, composition is drawn as a solid line with a filled diamond on the side of the whole. Aggregation uses a hollow diamond, and a plain association has no diamond at all. The numbers near the ends are the multiplicity: one chat thread contains many messages.

ChatThread is composed of Messages: filled diamond on the owner side
Loading diagram...

Read it from the diamond: ChatThread creates and owns its Message objects. Nothing else in the system refers to them.

3. A First Example: A Chat Thread and Its Messages

Here is the chat from the introduction as code. Look at how a message comes into existence. The caller never writes new Message(...). It just calls send() with a sender and some text, and the thread builds the Message object itself and keeps it in its own private list.

Output

Chat: Goa Trip (3 messages)
  #1 Anu: Tickets booked for Friday
  #2 Ravi: I will bring the speaker
  #3 Anu: Meet at Secunderabad station, 7 PM
Chat 'Goa Trip' deleted, messages left: 0

Three details make this composition and not something weaker.

First, the thread is the only place where messages are created. Because the constructor of Message is only called inside send(), there is no way for a message to exist without a chat.

Second, the list is private and is never returned. No other object gets a reference to a message, so no one can keep it alive after the chat is gone.

Third, the thread controls the end of life too. delete() clears the list, and since nothing else points to those Message objects, the garbage collector is free to reclaim them.

Life of the chat: the thread creates every message and they all go together
Loading diagram...

A note for Java developers: Java has no destructors, so "the part is destroyed" really means "the part becomes unreachable". If the whole is the only thing holding a reference to its parts, the parts become garbage at the same moment the whole does. That is why the next rule, never leaking a part, matters so much in Java.

4. Composition Can Be Nested

Real designs often have several levels of ownership. A question paper is made of sections, and each section is made of questions. Delete the paper and both levels go with it. Each class owns only the level directly below it, which keeps every class small and easy to reason about.

Two levels of composition: QuestionPaper owns Sections, each Section owns Questions
Loading diagram...

Notice that the outside world talks only to QuestionPaper. It never gets hold of a Section or a Question object. It asks the paper to add a question to a section by title, and the paper passes the request down.

Output

No section called Part C
Java Basics Test - total 15 marks
  Part A (4 marks)
    - What is a constructor? [2]
    - Difference between == and equals() [2]
  Part B (11 marks)
    - Reverse a string without a library method [5]
    - Find the second largest number in an array [6]

The request for "Part C" fails politely, because that section was never created. The paper decides which sections exist, and nobody can sneak one in from outside. Totals also roll up naturally: a section adds up its questions, and the paper adds up its sections.

5. Protect the Parts: Don't Leak Them

The easiest way to break composition by accident is a getter that returns the internal list. The moment you return it, someone else holds a reference to your parts and can change them behind your back. Here are two bank accounts that both create their own transaction records. Only one of them keeps those records safe.

Output

Leaky account records after clear(): 0
Safe account records after clear(): 2
  DEPOSIT 5000 -> balance 5000
  WITHDRAW 1200 -> balance 3800

LeakyAccount.getHistory() hands out the real list, so a single clear() from outside wipes the account's own records. SafeAccount.getStatement() builds a new list every time, so the caller can do anything it likes with its copy and the account is not affected.

Returning the real list lets callers change the parts. Returning a copy keeps ownership intact
Loading diagram...

In Java you have a few good options here: return a fresh copy as above, return List.copyOf(history) for an unmodifiable snapshot, or return Collections.unmodifiableList(history) if you want a read-only view that follows later changes. Whichever you pick, the rule is the same: the whole hands out information about its parts, never the parts themselves.

6. Favor Composition Over Inheritance

You will often hear the advice "favor composition over inheritance" from the Gang of Four design patterns book. The idea is simple. When you want to reuse a class, you can either extend it (inheritance) or keep an instance of it inside your class and use it privately (composition). Extending gives your class every public method of the parent, whether you want them or not. Composing lets you choose exactly what to expose.

Here is an undo history built both ways. BadHistory extends ArrayList, so anyone can call add(0, ...) and quietly put a fake step at the bottom of the history. UndoHistory keeps an ArrayDeque as a private field and exposes only record(), undo() and size().

Output

BadHistory: fake step, type Hello, make bold
Undo: make bold, then type Hello, then fake step
Undo: insert image
Undo: make bold
Steps left: 1
Inheritance exposes every list method. Composition hides the deque inside
Loading diagram...

The JDK itself has a famous example of this mistake. java.util.Stack extends Vector, so a "stack" in Java also lets you insert or remove items at any position, which a real stack should never allow. That is one reason the Java documentation recommends ArrayDeque for stack behaviour today.

Composition also makes behaviour easy to change later. If your class holds an interface type such as Deque<String> instead of extending a concrete class, you can switch the implementation without touching any caller.

7. Composition vs Aggregation vs Association

All three are "has-a" style relationships, so they are easy to mix up. The difference is about who creates the other object and whether it can live on its own.

AssociationAggregationComposition
MeaningA knows or uses BA groups B partsA owns B parts
Who creates BSomeone elseSomeone else, then passed inA itself
B after A is goneLives onLives onGone too
Can B be sharedYesYesNo
UML symbolPlain lineHollow diamondFilled diamond
ExampleStudent and CoursePlaylist and SongChatThread and Message

When you are not sure which one you have, walk through these questions in order:

Deciding between association, aggregation and composition
Loading diagram...

8. Common Mistakes

  • Accepting a ready-made part through a constructor and calling it composition. If the part was created outside and passed in, someone else may still hold it. That is aggregation.
  • Returning the internal collection from a getter. It turns your private parts into shared objects.
  • Putting the same part object into two wholes. A part belongs to one owner only.
  • Using inheritance just to reuse code, which drags every parent method into your class. Prefer a private field.
  • Making everything composition. If a part clearly has a life of its own, like a song in many playlists, forcing composition will make the design awkward.

Summary

  • Composition is the strongest "has-a" relationship: the whole creates, owns and ends the life of its parts.
  • In UML it is a solid line with a filled diamond on the whole's side.
  • In Java, keep the parts in private fields, create them inside the whole, and never return them directly. Return copies or read-only views instead.
  • Composition can be nested, with each class owning only the level below it.
  • Favor composition over inheritance when you only want to reuse behaviour, not promise an "is-a" relationship.

Once you get used to asking "who creates this object, and who else can see it?", choosing between association, aggregation and composition becomes a quick decision instead of a guess.