Composition: a Class That Has Another Class
Where you are: Week 0 review > Composition
Try This First
A Book needs an author with a first and last name. Decide whether to store the author as a separate Author object or as two loose String fields on Book, before reading on.
Reveal
Store an Author object. The author is its own thing with its own fields and behavior, so a Book should hold an Author (a “has-a” relationship). Book then delegates author work to the Author.
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.
See equals and hashCode for a full review of the prerequisite ideas.
Not sure? Take the 60-second self-check.
Try each from memory, then read the answer under it.
- Why does a class override
equals? To compare objects by their contents instead of by their arrows. - What rule ties
hashCodetoequals? Equal objects must return the samehashCode.
What You Need To Walk In With
Walk into the next class able to state these:
- Composition is a “has-a” relationship: a class holds a field whose type is another class you wrote.
- The composed object is stored, not recreated. The constructor receives it and saves it.
- The composed field shows up at every method that touches it: the constructor,
toString,equals,hashCode, andcompareTo. Each delegates to the part.
You should be able to: write a class that holds another class as a field and delegates to it at each of those sites.
How It Works
public final class Book implements Comparable<Book> {
private final String title;
private final Author author; // the composed field, a class you wrote
public Book(String title, Author author) {
if (title == null || title.isBlank()) { throw new IllegalArgumentException("bad title"); }
if (author == null) { throw new IllegalArgumentException("author required"); }
this.title = title;
this.author = author; // stored, not recreated
}
@Override
public String toString() {
return "Book[title=" + title + ", author=" + author + "]"; // calls Author.toString
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Book other)) { return false; }
return Objects.equals(title, other.title)
&& Objects.equals(author, other.author); // calls Author.equals
}
@Override
public int hashCode() {
return Objects.hash(title, author); // uses Author.hashCode
}
@Override
public int compareTo(Book other) {
int byAuthor = author.getLastName().compareTo(other.author.getLastName());
if (byAuthor != 0) { return byAuthor; }
return title.compareTo(other.title);
}
public Author getAuthor() { return author; }
}
The composed author appears at five sites: the constructor stores it, toString prints it, equals compares it, hashCode includes it, and compareTo reaches into it. Each one delegates to the Author, so Book never re-implements author logic. This five-site shape is the recurring shape of composition.
Worked Example: Predict, Then Check
Author a = new Author("Ada", "Lovelace");
Book b1 = new Book("Notes", a);
Book b2 = new Book("Notes", new Author("Ada", "Lovelace"));
System.out.println(b1.equals(b2));
Assuming Author overrides equals on its fields, predict the output.
Reveal
true. Book.equals compares the titles (equal) and delegates the author comparison to Author.equals, which finds the two authors equal by their fields. Delegation makes Book equality follow from its parts.
A Common Mistake
Re-implementing the part’s logic inside the whole, or skipping a delegation site, breaks composition. For example, comparing authors by re-reading their name strings in Book.equals instead of calling Author.equals duplicates logic and drifts out of sync. Store the object and delegate to it at every site. (Source: BJP (Reges and Stepp), Ch 8.)
Go Deeper (optional)
For the curious: building a whole from parts and wiring one behavior through another is the same shape as composing functions in mathematics, f(g(x)): the result of the inner part feeds the outer. A Book.equals that calls Author.equals is exactly that composition of behaviors, which is why the math tree links here through function-composition.
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
What is composition?
When a Book composes an Author, where does the author field appear?
How should Book.equals compare the author?
Book b1 and b2 have equal titles and equal-by-fields authors. With proper overrides, b1.equals(b2) is?