Aggregation

IB Syllabus: B3.1 – Implement aggregation (has-a relationship) between classes.

Table of Contents

  1. Key Concepts
    1. What is Aggregation?
    2. Aggregation vs Inheritance
    3. Arrays of Objects
    4. Delegation
    5. Nested Object Access
  2. Worked Examples
    1. Example 1: Design with Aggregation
    2. Example 2: Selection Sort on Object Array
  3. Quick Check
  4. Trace Exercise
  5. Spot the Error
  6. Predict the Output
  7. Practice Exercises
    1. Core
    2. Extension
    3. Challenge
  8. Connections

Key Concepts

What is Aggregation?

Aggregation is a relationship where one object contains another object as an attribute. This is a “has-a” relationship – a Library has Book objects, a School has Student objects, a Car has an Engine.

The containing object (the “whole”) stores a reference to the contained object (the “part”) and can call its methods. This lets you build complex systems from simpler components.

public class Address {
    private String street;
    private String city;
    private String country;

    public Address(String street, String city, String country) {
        this.street = street;
        this.city = city;
        this.country = country;
    }

    public String getCity() { return city; }
    public String getCountry() { return country; }
}

public class Student {
    private String name;
    private Address homeAddress;  // aggregation -- Student HAS-A Address

    public Student(String name, Address homeAddress) {
        this.name = name;
        this.homeAddress = homeAddress;
    }

    public String getName() { return name; }

    // Delegation -- calling a method on the contained object
    public String getCity() {
        return homeAddress.getCity();
    }
}
Address addr = new Address("123 Main St", "Bangkok", "Thailand");
Student s = new Student("Alice", addr);
System.out.println(s.getName() + " lives in " + s.getCity());

Output:

Alice lives in Bangkok

The Student object does not know how addresses work internally – it just calls homeAddress.getCity(). This is delegation – passing responsibility to the contained object.

Aggregation vs Inheritance

These are the two fundamental ways classes relate to each other. Using the wrong one causes design problems.

  Aggregation (has-a) Inheritance (is-a)
Relationship One object contains another One class extends another
Example A Car has an Engine A Dog is an Animal
Code private Engine engine; class Dog extends Animal
Coupling Loose – parts are independent Tight – child depends on parent
Polymorphism Not supported Supported
Flexibility Parts can be swapped at runtime Fixed at compile time

To decide between aggregation and inheritance, ask: “Is A a type of B?” If yes, use inheritance (Dog IS-A Animal). If no, ask: “Does A contain B?” If yes, use aggregation (Car HAS-A Engine). Never use inheritance just to reuse code – use aggregation instead.

Arrays of Objects

The most common form of aggregation in IB exams is a class that manages an array of objects. This is where the exam method patterns (filter, accumulate, sort) apply.

public class Library {
    private String name;
    private Book[] books;
    private int bookCount;

    public Library(String name, int capacity) {
        this.name = name;
        this.books = new Book[capacity];
        this.bookCount = 0;
    }

    public void addBook(Book book) {
        if (bookCount < books.length) {
            books[bookCount] = book;
            bookCount++;
        }
    }

    // Filter pattern: find books matching a condition
    public void displayAvailable() {
        for (int i = 0; i < bookCount; i++) {
            if (books[i] != null && !books[i].isOnLoan()) {
                System.out.println(books[i].getTitle());
            }
        }
    }

    // Accumulate pattern: count items matching a condition
    public int countByAuthor(String author) {
        int count = 0;
        for (int i = 0; i < bookCount; i++) {
            if (books[i] != null && books[i].getAuthor().equals(author)) {
                count++;
            }
        }
        return count;
    }
}

Always check for null before calling methods on array elements. Even with a bookCount tracker, defensive null checks protect against bugs where elements are removed or the array is sparsely populated. This null check earns a mark in nearly every IB exam method question.

Delegation

When the “whole” object needs information from a “part”, it delegates by calling a method on the contained object rather than accessing the data directly:

public class Car {
    private Engine engine;

    public Car(Engine engine) {
        this.engine = engine;
    }

    // Delegation: Car asks Engine for its horsepower
    public int getHorsepower() {
        return engine.getHorsepower();
    }

    // Delegation with logic
    public boolean isPowerful() {
        return engine.getHorsepower() > 200;
    }
}

The Car does not store horsepower itself – it asks its Engine. If the engine is replaced, the car automatically reports the new horsepower. This keeps each class responsible for its own data.

Nested Object Access

Sometimes you need to access data several levels deep. Each step calls a method on the returned object:

// lawyer.getCases()[i].getDaysFromStart()
//   ↑ get the array    ↑ get one case    ↑ get its duration

This pattern appears frequently in IB P2 questions – you traverse from a container to its parts to the parts’ attributes, chaining getter calls.


Worked Examples

Example 1: Design with Aggregation

Scenario: A Gym has members. Each member has a name and membership type. The gym needs to count premium members and find a member by name.

UML:

+----------------------------------+       +---------------------------+
|              Gym                 |       |          Member           |
+----------------------------------+       +---------------------------+
| - name: String                   |       | - name: String            |
| - members: Member[]              |       | - type: String            |
| - memberCount: int               |       +---------------------------+
+----------------------------------+       | + Member(String, String)  |
| + Gym(String, int)               |◇------| + getName(): String       |
| + addMember(Member): void        |       | + getType(): String       |
| + countPremium(): int            |       | + isPremium(): boolean    |
| + findByName(String): Member     |       +---------------------------+
+----------------------------------+

Java:

public class Member {
    private String name;
    private String type;

    public Member(String name, String type) {
        this.name = name;
        this.type = type;
    }

    public String getName() { return name; }
    public String getType() { return type; }
    public boolean isPremium() { return type.equals("premium"); }
}

public class Gym {
    private String name;
    private Member[] members;
    private int memberCount;

    public Gym(String name, int capacity) {
        this.name = name;
        this.members = new Member[capacity];
        this.memberCount = 0;
    }

    public void addMember(Member m) {
        if (memberCount < members.length) {
            members[memberCount] = m;
            memberCount++;
        }
    }

    // Accumulate pattern
    public int countPremium() {
        int count = 0;
        for (int i = 0; i < memberCount; i++) {
            if (members[i] != null && members[i].isPremium()) {
                count++;
            }
        }
        return count;
    }

    // Find-by-attribute pattern
    public Member findByName(String name) {
        for (int i = 0; i < memberCount; i++) {
            if (members[i] != null && members[i].getName().equals(name)) {
                return members[i];
            }
        }
        return null;  // not found
    }
}

Example 2: Selection Sort on Object Array

Sort members alphabetically by name (ascending):

public void sortByName() {
    for (int i = 0; i < memberCount - 1; i++) {
        int minIndex = i;
        for (int j = i + 1; j < memberCount; j++) {
            if (members[j].getName().compareTo(members[minIndex].getName()) < 0) {
                minIndex = j;
            }
        }
        // Swap entire objects
        Member temp = members[i];
        members[i] = members[minIndex];
        members[minIndex] = temp;
    }
}

When sorting objects by a String attribute, use .compareTo() instead of < or >. It returns a negative number if the first string comes before the second alphabetically, zero if equal, and positive if it comes after.


Quick Check

Q1. What type of relationship is aggregation?

Q2. What is delegation in OOP?

Q3. A Car contains an Engine. Should this be aggregation or inheritance?

Q4. What does findByName("Charlie") return if Charlie is not in the array?


Trace Exercise

Trace the Gym class methods. A gym has 3 members: Alice (premium), Bob (basic), Charlie (premium).

Trace: Aggregation Methods

Gym g = new Gym("FitLife", 10);
g.addMember(new Member("Alice", "premium"));
g.addMember(new Member("Bob", "basic"));
g.addMember(new Member("Charlie", "premium"));
Method callResult
g.countPremium()
g.findByName("Bob").getType()
g.findByName("Dave")

Spot the Error

This method crashes with a NullPointerException. Click the buggy line, then pick the fix.

1public void printAllTitles() { 2 for (int i = 0; i < books.length; i++) { 3 System.out.println(books[i].getTitle()); 4 } 5}

Pick the fix:


Predict the Output

What does countPremium() return for a gym with members Alice (premium), Bob (basic), Charlie (premium)?


Practice Exercises

Core

  1. Design two classes – Create a Course class (name, teacher, maxStudents) and a School class that manages an array of Courses. Write addCourse(), findCourse(String name), and countAvailable() (courses with fewer than maxStudents enrolled).

  2. Aggregation identification – For each pair, state whether the relationship is aggregation (has-a) or inheritance (is-a):
    • (a) Playlist and Song
    • (b) ElectricCar and Car
    • (c) Hospital and Doctor
    • (d) Rectangle and Shape
  3. Read and trace – Given the Library and Book classes from this page, trace what happens when you add 3 books, check out one of them, then call countByAuthor().

Extension

  1. Selection sort – Add a sortByPrice(Product[] products, int count) method that sorts products by price in descending order (highest first). Use selection sort and getter methods.

  2. Nested access – A Department has an array of Employee objects. Each Employee has a Salary object with getBase() and getBonus() methods. Write a method totalPayroll() in Department that sums base + bonus for all employees.

Challenge

  1. Full system – Design and implement a Hotel system with Room and Guest classes. A Hotel manages rooms. Each room has a number, type (“single”/”double”), price, and an optional Guest. Implement: check-in (assign guest to room), check-out (remove guest), find available rooms by type, and calculate total revenue from occupied rooms.

Connections

  • Prerequisites: Classes and Objects – creating and using objects
  • Prerequisites: UML Class Diagrams – the diamond (◇) notation for has-a
  • Prerequisites: Object References – null checks, reference behaviour
  • Next: Inheritance – the other fundamental relationship (is-a)
  • Forward: OOP Exam Patterns – the filter, accumulate, and sort patterns used here are exam essentials

Back to top

© EduCS.me — A resource hub for IB Computer Science

This site uses Just the Docs, a documentation theme for Jekyll.