00:00

Aggregation in Java – HAS-A Relationship Explained

Aggregation is a HAS-A relationship in which one object (the whole) holds references to other objects (the parts), but the parts can exist independently of the whole. A department has employees, yet employees continue to exist if the department is closed, and one employee may belong to several departments.

Aggregation is one of the three forms of object association, alongside plain association and composition. This tutorial explains all three with examples and shows when HAS-A is a better design than inheritance.

1. Association, Aggregation and Composition

Objects relate to each other in increasingly strong ways:

RelationshipMeaningOwnership / life cycleExample
AssociationObjects use or know each otherNoneDoctor – Patient
Aggregation (weak HAS-A)Whole contains partsParts live independently and can be sharedDepartment – Employee
Composition (strong HAS-A)Whole owns partsParts are created by and die with the wholeOrder – OrderLine, House – Room

In UML diagrams, aggregation is drawn with a hollow diamond on the whole's side, and composition with a filled diamond.

2. Aggregation Example

AggregationDemo.java
import java.util.ArrayList;
import java.util.List;

public class AggregationDemo {
    // Part: exists independently of any Department
    static class Employee {
        final String name;
        Employee(String name) { this.name = name; }
        public String toString() { return name; }
    }

    // Whole: HAS-A list of employees, but does not own their life cycle
    static class Department {
        final String name;
        private final List<Employee> members = new ArrayList<>();
        Department(String name) { this.name = name; }
        void add(Employee e) { members.add(e); }
        void remove(Employee e) { members.remove(e); }
        public String toString() { return name + " " + members; }
    }

    public static void main(String[] args) {
        Employee asha = new Employee("Asha");      // created outside the department
        Employee ravi = new Employee("Ravi");

        Department backend = new Department("Backend");
        Department platform = new Department("Platform");
        backend.add(asha);
        backend.add(ravi);
        platform.add(asha);                        // the same employee can belong to two departments

        System.out.println(backend);
        System.out.println(platform);

        backend = null;                            // department dissolved...
        System.out.println("Asha still exists: " + asha + ", member of " + platform);
    }
}
Output
Backend [Asha, Ravi]
Platform [Asha]
Asha still exists: Asha, member of Platform [Asha]
  • Employees are created outside the department and passed in.
  • The same Employee object is shared by two departments.
  • Discarding a department does not destroy its employees.

3. Composition Example

In composition, the whole creates and controls its parts. Here an order's lines are created inside the order and cannot be reached or shared from outside:

CompositionDemo.java
import java.util.ArrayList;
import java.util.List;

public class CompositionDemo {
    // Composition: the Order creates and owns its lines; they cannot exist alone
    static class Order {
        private final String id;
        private final List<Line> lines = new ArrayList<>();

        Order(String id) { this.id = id; }

        void addLine(String product, int qty, double price) {
            lines.add(new Line(product, qty, price));   // created by the whole
        }
        double total() { return lines.stream().mapToDouble(Line::amount).sum(); }

        // The part type is private: nobody outside can create or share a Line
        private record Line(String product, int qty, double price) {
            double amount() { return qty * price; }
        }

        public String toString() { return "Order " + id + ": " + lines.size() + " lines, total " + total(); }
    }

    public static void main(String[] args) {
        Order order = new Order("A-17");
        order.addLine("Keyboard", 1, 2499);
        order.addLine("Mouse", 2, 799);
        System.out.println(order);
        // When the order is discarded, its lines go with it
    }
}
Output
Order A-17: 2 lines, total 4097.0

Typical signs of composition: the whole creates the parts itself, the part type is often private or nested, and parts are never handed out for reuse.

4. Plain Association

The weakest relationship: objects interact, often only through method parameters, without one containing the other:

AssociationDemo.java
public class AssociationDemo {
    // Plain association: objects use each other, no ownership at all
    static class Doctor {
        final String name;
        Doctor(String name) { this.name = name; }
        void treat(Patient p) { System.out.println("Dr. " + name + " treats " + p.name); }
    }
    static class Patient {
        final String name;
        Patient(String name) { this.name = name; }
    }

    public static void main(String[] args) {
        Doctor d = new Doctor("Mehta");
        Patient p1 = new Patient("Asha"), p2 = new Patient("Ravi");
        d.treat(p1);
        d.treat(p2);
    }
}
Output
Dr. Mehta treats Asha
Dr. Mehta treats Ravi

5. HAS-A vs IS-A

AspectInheritance (IS-A)Aggregation / composition (HAS-A)
Java mechanismextends / implementsA field referencing another object
CouplingTight: subclass depends on parent internalsLoose: uses only the part's public API
FlexibilityFixed at compile timeParts can be swapped at run time
ReuseInherits everythingExposes only what the whole chooses
ExampleCar extends VehicleCar has an Engine

The guideline "favour composition over inheritance" applies here: if you cannot honestly say "X is a Y", use a HAS-A relationship. See Inheritance.

6. When to Use Aggregation

  • The parts have meaning on their own (employees, students, products in a catalogue).
  • Parts may be shared between several wholes.
  • Parts are created elsewhere, for example loaded from a database or injected by a framework.

In Spring applications, a service holding references to injected repositories is aggregation: the container, not the service, owns their life cycle.

7. Summary

  • Aggregation is a weak HAS-A relationship: the whole references parts that live independently.
  • Composition is a strong HAS-A relationship: the whole creates and owns its parts.
  • Association is the general "uses / knows" relationship.
  • Prefer HAS-A over IS-A when the relationship is not a true specialisation.

Related Tutorials