← Skill tree CS Skill Tree 0 CSCD211

Composition: Has-A, Owns Lifetime

Textbook: BJP (Reges and Stepp)

Before You Start

Check each box you can do from memory. A box you cannot check yet is not a problem; it points you to a quick refresher, not a grade.

Not sure? Take the 60-second self-check.

1. Aggregation marker. A constructor body contains this.engine = engine; where engine is a parameter. What relationship is this?

Check

Aggregation. The engine was constructed outside and received; the container did not build it.

2. Composition marker. What distinguishes composition from aggregation in the constructor body?

Check

Composition uses new to construct the contained object inside the container. The container builds the object; no external reference to that specific instance exists at creation time.

3. Lifetime question. For a composed object, what happens when the container is garbage-collected?

Check

The composed object becomes unreachable (assuming no references were leaked) and is also eligible for garbage collection. Its lifetime is bounded by the container’s.


Try This First

public class Car {
    private final Engine engine;

    public Car(final EngineSpecs specs) {
        this.engine = new Engine(specs);
    }
}

Before reading: who holds a reference to this specific Engine instance after the constructor returns?

Check

Only the Car object. specs is a parameter that describes what engine to build, not the engine itself. The new Engine(specs) call creates a fresh instance that is stored only in this.engine. No external code has a reference to this specific Engine.


What You Need To Walk In With

The Insight: Composition is “has-a, built internally, owns the lifetime.” The marker is new (or a defensive copy) in the constructor: the container builds its part; the part does not pre-exist. Because no external reference was ever handed out for this specific object, the part belongs exclusively to the container. When the container goes, the part goes with it.

The single diagnostic question is “who constructed this object?” If the container did, using new inside its own constructor body, the relationship is composition. If the object arrived from outside (as a parameter that was stored directly), the relationship is aggregation, regardless of what field it is stored in. You can trace ownership and lifetime in any constructor by following that one question through every field assignment. You can: identify composition by spotting new inside a constructor body, explain why the composed object cannot outlive its container, convert an aggregation constructor to composition using a defensive copy, and distinguish composition from aggregation in the S20 Lab 3 Book constructor.


How It Works

The composition marker

// COMPOSITION: constructed internally
public Car(final EngineSpecs specs) {
    this.engine = new Engine(specs);   // new instance, owned by Car
}

// AGGREGATION: received from outside
public Car(final Engine engine) {
    this.engine = engine;              // same instance as caller's reference
}

In the composition form, Engine is built here, inside the constructor. No other code was handed a reference to this specific Engine object. The Car exclusively owns it.

S20 Lab 3: Book owns its Publisher and Authors

From S20.Lab3.Book.java (the canonical 211 composition example):

public Book(final String title, final Publisher pubs, final Author[] author)
{
    this.title = title;

    // Composition: Book constructs its own Publisher from the passed-in data
    this.pub = new Publisher(pubs.getPubName(), pubs.getPubCity());

    // Composition: Book constructs each Author element
    this.authors = new Author[author.length];
    for (int x = 0; x < author.length; x++) {
        String[] name = author[x].getName().split(",");
        this.authors[x] = new Author(name[0].trim(), ...);
    }
}

Even though pubs and author are passed in as parameters, the Book does NOT store those references. It reads data from them and constructs fresh Publisher and Author instances using new. The caller’s pubs object and the Book’s this.pub object are completely separate instances with independent lifetimes.

Why not this.pub = pubs;?

If the constructor assigned this.pub = pubs, the caller could later call pubs.setPubName("Different") and break the Book’s invariant. By constructing a new Publisher from the data, the Book isolates its state from the caller. This is the defensive copy pattern.

Lifetime exclusivity

Because no external code received a reference to the Book’s internal Publisher or Author instances, only the Book holds references to them. When the Book is garbage-collected, those objects become unreachable and are collected too. The part’s lifetime ends with the whole.


Quick check

Check your understanding

In the S20 Lab 3 Book constructor, this.pub = new Publisher(pubs.getPubName(), pubs.getPubCity()) is used instead of this.pub = pubs. What single word in the constructor body is the composition marker?

Tier 1 · BJP (Reges and Stepp), Ch 8


Worked Example: Predict Then Check

Publisher original = new Publisher("O'Reilly", "Sebastopol");
Book b = new Book("Effective Java", original, authors);

original.setPubName("Different Press");

System.out.println(b.getPublisher().getPubName());

Predict the output: "O'Reilly" or "Different Press"?

Reasoning

b was constructed with composition: this.pub = new Publisher(original.getPubName(), original.getPubCity()). The Book’s publisher is a different object from original. Mutating original does not affect this.pub.

Show answer

"O'Reilly". The Book has its own Publisher instance, copied from the data in original. Changing original has no effect on the Book’s internal state.

Source: S20.Lab3.Book.java.


Common Misconceptions

Misconception 1: assigning a parameter to a field and calling it composition

Wrong mental model: “I receive an Engine, store it in a field, and call it composition because I HAS-A Engine.”

Why it breaks: The ownership claim fails. The caller still holds a reference to the same Engine. If the caller mutates it, the stored reference sees the mutation. If another object also receives the same Engine, both share state. Composition requires exclusive ownership, which requires the container to have constructed the object.

How to correct: If isolation is needed, either: (a) construct internally (this.engine = new Engine(specs)) or (b) defensively copy (this.engine = new Engine(received.getX(), received.getY())). Both break the shared reference.

Source: Bloch, Effective Java, Item 50; UML composition semantics.


Misconception 2: calling singleton or factory-obtained references composition

Wrong mental model:this.logger = LoggerFactory.getInstance() is composition because the logger is in a field.”

Why it breaks: The same Logger instance is shared with every other class that calls the same factory. The logger’s lifetime is governed by the factory (or the JVM), not by this class. Exclusive ownership does not exist. This is aggregation or association, not composition.

How to correct: Composition implies no other references to the specific instance at construction time. Shared singletons or factory-obtained objects cannot satisfy that.


Quick check

Check your understanding

A student writes this.engine = engine; in a constructor (where engine is a parameter) and labels the relationship composition. What is the error in that reasoning?

Tier 2 · BJP (Reges and Stepp), Ch 8


Formal Definition

UML 2.5 defines composition as a binary association with the filled diamond notation on the container (whole) side, representing a “strong” form of aggregation where the parts are created and destroyed along with the whole. The contained object’s lifetime is strictly bounded by the container’s. In Java, the implementation is: new inside the constructor body (or a defensive copy that extracts data from a parameter and builds a fresh object).


Mental Model

Composition is like a factory floor assembling a product. The factory builds each component from raw materials; the components are not borrowed or rented from another source. When the product leaves the factory, its components go with it. If the product is destroyed, the components are destroyed with it. No one outside the factory has access to those specific components.


Connections

Within CSCD 210/211: S20 Lab 3 is the canonical CSCD 211 composition example. Every new in a constructor is a composition marker. Defensive Copy in the Constructor deepens this with the technique that turns received data into exclusive composition.

Looking back: Aggregation: Has-A, Can Outlive covers received references. This lesson covers constructed references. The difference is ownership and isolation.

Looking ahead: Declaring and Initializing a Class-Type Field covers a class with a class-type field in detail. Defensive Copy in the Constructor covers the technique that turns received data into exclusive composition.


Go Deeper (optional)

You do not need any of this to write correct composition code. If the ideas so far feel solid and you are curious where they lead, read on.

Composition and career code. In professional Java, most final immutable value objects use composition throughout: every field is built inside the constructor from the data passed in, so no external mutation path can ever reach the object’s internals. When you read a well-designed library class and notice that every field is private final and the constructor calls new for each one, you are seeing this pattern applied consistently. Bloch’s Item 50 in Effective Java (“Make defensive copies when needed”) formalizes exactly the move from aggregation to composition that S20 Lab 3 demonstrates.

The design-pattern connection. The “prefer composition over inheritance” principle (Bloch Item 18, Gang of Four) is not just a style preference: it is the observation that wiring behaviors together through HAS-A relationships is more flexible and easier to test than baking them in through IS-A inheritance. The container “owns” the behavioral pipeline the same way a mathematical composition owns the function chain. A class that composes a Strategy object and delegates a decision to it is using the Strategy pattern, which is the simplest form of a first-class function in an object-oriented language.

The type-theory reading. If you encounter type theory in later coursework, the HAS-A / IS-A split maps directly onto product types versus sum types (or subtype relationships). A class with two composed fields is a product of those two types: to build one, you must supply both. Inheritance (IS-A) is closer to a subtype relationship. The ownership and lifetime rules of composition are exactly why product-type reasoning applies: both components exist for exactly as long as the product does.

The historical root. The principle that a module should own exactly what it needs to fulfill its responsibility and nothing more traces to David Parnas’s 1972 paper “On the Criteria To Be Used in Decomposing Systems into Modules.” Parnas argued for information hiding: each module controls its own internal data and exposes only a clean interface. Composition in UML and Java is one concrete mechanism for enforcing that principle at the object level.


Practice

Level 1

Classify the Publisher relationship in the following Book constructor as aggregation or composition:

public Book(final String title, final Publisher pubs) {
    this.title = title;
    this.pub = new Publisher(pubs.getPubName(), pubs.getPubCity());
}
Show answer

Composition. this.pub is a fresh Publisher instance constructed from the data of pubs. The caller’s pubs object and this.pub are different instances. The Book exclusively owns its Publisher.

Source: S20.Lab3.Book.java.


Level 2

Rewrite the following aggregation constructor as composition:

public class Course {
    private final List<Student> students;

    public Course(final List<Student> students) {
        this.students = students;   // aggregation
    }
}
Show answer
public class Course {
    private final List<Student> students;

    public Course(final List<Student> students) {
        this.students = new ArrayList<>(students);  // composition: defensive copy
    }
}

new ArrayList<>(students) creates a fresh list containing the same Student references. The caller’s original list and this.students are now independent list objects. Mutations to the caller’s list do not affect the Course.

Note: the Student objects inside both lists are the same instances (shallow copy). To compose the students themselves (not just the list), element-by-element copying would be needed.


Level 3

In S20 Lab 3, the Book constructor takes a Publisher pubs parameter and does NOT store it directly. It creates a new Publisher(pubs.getPubName(), pubs.getPubCity()). Explain in 3-4 sentences why this design choice matters, using the terms “composition,” “aggregation,” “isolation,” and “invariant.”

Show answer

The Book constructor uses composition: it builds a fresh Publisher from the data in pubs rather than storing pubs directly. If the constructor used aggregation (this.pub = pubs), the caller could later mutate pubs and silently change the Book’s publisher without the Book’s knowledge, breaking the Book’s invariant (that its publisher data is stable after construction). Composition provides isolation: the Book’s Publisher instance is exclusively owned by the Book and cannot be modified through any external reference. This invariant is what makes the Book a reliable, encapsulated object.


Check Yourself

Close the notes and answer each one from memory, then reveal it. Pulling an idea back from memory is one of the strongest ways to make it stick.

Check your understanding

Which of the following constructor bodies shows composition (not aggregation) for the engine field?

Tier 1 · BJP (Reges and Stepp), Ch 8

In S20 Lab 3, the Book constructor receives a Publisher pubs parameter but executes this.pub = new Publisher(pubs.getPubName(), pubs.getPubCity()). What relationship does this create between the Book and its Publisher?

Tier 1 · BJP (Reges and Stepp), Ch 8

Trace this code and predict the output: ``java Publisher original = new Publisher("O'Reilly", "Sebastopol"); Book b = new Book("Effective Java", original, authors); original.setPubName("Different Press"); System.out.println(b.getPublisher().getPubName()); ` The Book constructor uses: this.pub = new Publisher(pubs.getPubName(), pubs.getPubCity());`

Tier 2 · BJP (Reges and Stepp), Ch 8

A developer writes this.logger = LoggerFactory.getInstance() in a constructor and claims this is composition. What is wrong with that claim?

Tier 2 · BJP (Reges and Stepp), Ch 8

Why does composition guarantee that the contained object is garbage-collected when the container is garbage-collected (assuming no reference leaks)?

Tier 3 · BJP (Reges and Stepp), Ch 8