Course topics

By WebNest Studio Engineering Team

Java - Core Tutorial

Aggregation in Java

Aggregation is a specialized form of association that models a "has-a" relationship between two classes, where the contained object's lifecycle is independent of the container. This is often described as a "weak" or "loose" ownership relationship — the whole can be destroyed without destroying its parts.

A classic example is a Department that has Employees. An Employee can be transferred to another department, or can exist in the system even if a particular Department object is deleted — the Employee was never truly "owned" by that one Department. This independence is exactly what distinguishes aggregation from its stronger sibling, composition.

Why it matters

Most relationships between classes are "has a", not "is a": an order has items, a student has an address. Modelling them as fields rather than inheritance keeps classes independent and reusable. Whether the contained object can outlive its owner is the difference between aggregation and composition, and it is a common design question in interviews.

Watch: Aggregation in Java Explained | HAS-A Relationship with Real Examples | Java Core Course

A full walkthrough of aggregation: the HAS-A relationship compared with IS-A, a Department that has Employees, why the parts stay independent, how it differs from composition, when to use it, and common interview questions.

Watch on YouTube

HAS-A vs IS-A

Java classes relate to each other in two basic ways, and asking which one applies is the first step in any class design:

  • IS-A is inheritance. A Manager IS-A Employee, so class Manager extends Employee. The child gets the parent's fields and methods and can be used anywhere the parent is expected.
  • HAS-A is a field. An Employee HAS-A Address, so Employee declares a field of type Address. Nothing is inherited; the employee just keeps a reference and calls the address's methods when it needs them.
  • A quick test: say the sentence out loud. "An employee is an address" is clearly wrong, so extends would be wrong too, while "an employee has an address" sounds right.
  • HAS-A is the more flexible choice. You can swap the referenced object at runtime, share it between several owners, and change the Address class without breaking a class hierarchy. That is why the common advice is "favour composition over inheritance", where composition here means HAS-A in general.

Aggregation vs Composition

Both aggregation and composition are "has-a" relationships implemented in Java the same syntactic way — one class holds a reference to another as a field. The difference is purely conceptual/lifecycle-based, not something the compiler enforces.

  • Aggregation (weak ownership) — the contained object can exist independently and can even be shared between multiple containers. Example: a Department "has" Employees, but Employees exist without any particular Department.
  • Composition (strong ownership) — the contained object's lifecycle is tied to the container; it is typically created inside the container and destroyed with it. Example: a House "has" Rooms, and a Room stops making sense if the House object is destroyed.
  • In code, aggregation is usually shown by passing an already-existing object into a constructor or setter, while composition is usually shown by the container creating the contained object internally (often with "new" inside its own constructor).

How Aggregation Protects the Part Objects

In aggregation, the container only stores a reference to an object that was created somewhere else. Removing the container, or setting its variable to null, removes that one reference and nothing else. The garbage collector frees an object only when no live reference to it is left, so the part stays alive as long as any other code still points to it.

This is what keeps the parts safe. Deleting a Department does not delete its employees. One Address object can be shared by several Employee objects, and it is still usable after all of them are gone.

  • The part is created outside the container and passed in through a constructor or setter.
  • Several containers can hold references to the same part at the same time.
  • When a container is gone, the part survives as long as another reference to it exists.

Why This Distinction Matters

Recognizing aggregation versus composition helps you design classes that reflect real-world lifecycle rules. Modeling a relationship as aggregation when it should be composition (or vice versa) can lead to objects that are deleted too aggressively (losing data that should persist independently) or objects that linger unnecessarily (memory leaks, orphaned references) when they should have been cleaned up with their container.

When to Use Aggregation in Real Projects

Choose aggregation when the part has a life and an identity of its own, outside the object that currently holds it. Typical signs:

  • The part is stored or managed separately, for example employees in their own database table, so they must survive when a department is closed.
  • The part can move between owners: an employee transfers to another department, a player joins another team.
  • The same part is shared: one Address used by several employees, one Course listed in several programs.
  • The part is supplied from outside, through a constructor or setter. Spring's dependency injection works this way: a service receives a repository that the framework created, and many services can share it.

Examples

Aggregation: Employees are created independently and merely referenced by Department

Java
import java.util.ArrayList;
import java.util.List;

class Employee {
    String name;
    Employee(String name) { this.name = name; }
}

class Department {
    String deptName;
    List<Employee> employees = new ArrayList<>();

    Department(String deptName) {
        this.deptName = deptName;
    }

    void addEmployee(Employee e) { // Employee created OUTSIDE, just referenced here
        employees.add(e);
    }
}

public class AggregationDemo {
    public static void main(String[] args) {
        Employee e1 = new Employee("Riya");
        Employee e2 = new Employee("Karan");

        Department engineering = new Department("Engineering");
        engineering.addEmployee(e1);
        engineering.addEmployee(e2);

        System.out.println(engineering.deptName + " has " + engineering.employees.size() + " employees");

        // Department is discarded, but the Employee objects still exist independently
        engineering = null;
        System.out.println(e1.name + " still exists after the department reference is gone");
    }
}
Output
Engineering has 2 employees
Riya still exists after the department reference is gone

One Address shared by two Employees outlives both of them

Java
public class SharedPartDemo {
    public static void main(String[] args) {
        Address office = new Address("Pune");

        Employee riya = new Employee("Riya", office);
        Employee karan = new Employee("Karan", office);

        System.out.println(riya.name + " works in " + riya.address.city);
        System.out.println(karan.name + " works in " + karan.address.city);

        riya = null;  // one owner is gone
        karan = null; // and the other one too
        System.out.println("Address still usable: " + office.city);
    }
}

class Address {
    String city;
    Address(String city) { this.city = city; }
}

class Employee {
    String name;
    Address address; // HAS-A Address, but does not own it

    Employee(String name, Address address) {
        this.name = name;
        this.address = address; // receives an existing object
    }
}
Output
Riya works in Pune
Karan works in Pune
Address still usable: Pune

Aggregation next to composition: an employee transfers, rooms belong to one house

Java
import java.util.ArrayList;
import java.util.List;

public class HasADemo {
    public static void main(String[] args) {
        Employee riya = new Employee("Riya");

        Department sales = new Department("Sales");
        Department support = new Department("Support");

        sales.hire(riya);
        System.out.println(riya.name + " is in " + sales.name + ": " + sales.staff.contains(riya));

        // Transfer: the same Employee object moves to another Department
        sales.leave(riya);
        support.hire(riya);
        System.out.println(riya.name + " is in " + support.name + ": " + support.staff.contains(riya));

        sales = null;    // closing a department does not touch its former staff
        System.out.println(riya.name + " still exists");

        House house = new House(3);
        System.out.println("House has " + house.roomCount() + " rooms");
        house = null;    // the rooms had no other reference, so they go with the house
    }
}

class Employee {
    String name;
    Employee(String name) { this.name = name; }
}

// Aggregation: Department receives Employees that already exist
class Department {
    String name;
    List<Employee> staff = new ArrayList<>();

    Department(String name) { this.name = name; }

    void hire(Employee e)  { staff.add(e); }
    void leave(Employee e) { staff.remove(e); }
}

// Composition: House creates its own Rooms, nobody else can reach them
class House {
    private final List<String> rooms = new ArrayList<>();

    House(int roomCount) {
        for (int i = 1; i <= roomCount; i++) {
            rooms.add("Room " + i);   // created inside, owned by this House
        }
    }

    int roomCount() { return rooms.size(); }
}
Output
Riya is in Sales: true
Riya is in Support: true
Riya still exists
House has 3 rooms

Common Mistakes

  • Treating aggregation and composition as if Java has separate keywords or syntax for them — both are plain object references; the difference is design intent and lifecycle management.
  • Modeling a strongly-owned part (like a Room that only makes sense inside one House) as aggregation, letting it be shared or outlive its container incorrectly.
  • Assuming aggregation implies a collection (like a List) is always involved — aggregation can just as easily be a single object reference.
  • Forgetting that in aggregation, the same contained object can legitimately belong to multiple containers at once (an Employee could be referenced by a Project as well as a Department).

Exercise

Model a HAS-A relationship: a Student has an Address. The address is created separately and passed to the student.

Expected output: Asha lives in Gurugram

Java · starter code
class Address {
    String city;

    Address(String city) {
        this.city = city;
    }
}

class Student {
    String name;
    // TODO: a field that refers to an Address

    // TODO: a constructor taking a name and an Address

    // TODO: describe() returns "<name> lives in <city>"
}

public class StudentDemo {
    public static void main(String[] args) {
        Address address = new Address("Gurugram");
        Student student = new Student("Asha", address);
        System.out.println(student.describe());
    }
}
Show hints
  • A HAS-A relationship is just a field whose type is another class.
  • Reach the city through the field: address.city.
Show solution
Java · solution
class Address {
    String city;

    Address(String city) {
        this.city = city;
    }
}

class Student {
    String name;
    Address address;

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

    String describe() {
        return name + " lives in " + address.city;
    }
}

public class StudentDemo {
    public static void main(String[] args) {
        Address address = new Address("Gurugram");
        Student student = new Student("Asha", address);
        System.out.println(student.describe());
    }
}

Quick quiz

1. A Department refers to Employee objects that were created elsewhere and continue to exist if the department is removed. What is this?

  • A. Inheritance
  • B. Aggregation
  • C. Composition
  • D. Polymorphism
Show answer

B. In aggregation the contained objects have an independent lifetime; the container only refers to them.

2. A House creates its Room objects itself, and they have no meaning without the house. What is this?

  • A. Aggregation
  • B. Inheritance
  • C. Composition
  • D. Association with no ownership
Show answer

C. Composition is the stronger form: the owner creates and controls the parts, and they do not outlive it.

3. Which phrase describes the relationship that aggregation and composition both model?

  • A. IS-A
  • B. EXTENDS-A
  • C. USES-A
  • D. HAS-A
Show answer

D. Both are HAS-A relationships, implemented with a field. IS-A is inheritance.

Interview questions

What is the difference between aggregation and composition?

Show answer

Both mean one object holds a reference to another. In aggregation the part can exist on its own: employees exist whether or not a department does, so the part is usually passed in from outside. In composition the part belongs to the whole and is created and destroyed with it: an order line has no meaning without its order, so the owner creates it internally.

Why is composition often preferred over inheritance?

Show answer

Inheritance ties a subclass to the implementation of its parent, so a change in the parent can break the child, and the relationship is fixed at compile time. Composition builds behaviour from independent objects that communicate through their public methods: parts can be swapped, tested separately and reused in classes that have nothing else in common.

Key Points to Remember

  • Aggregation models a "has-a" relationship with weak ownership — the part can exist independently of the whole.
  • Composition also models "has-a" but with strong ownership — the part's lifecycle depends on the whole.
  • The distinction is about design intent and object lifecycle, not different Java syntax.
  • Aggregated objects are typically created outside the container and passed in, rather than created internally by the container.
  • IS-A means inheritance (extends); HAS-A means a field that refers to another object. Prefer HAS-A unless the IS-A sentence is genuinely true.
  • Use aggregation when the part can be shared, transferred or stored on its own, as with employees and departments.

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering