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.
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
ManagerIS-AEmployee, soclass 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
EmployeeHAS-AAddress, soEmployeedeclares a field of typeAddress. 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
extendswould 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
Addressclass 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
Addressused by several employees, oneCourselisted 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
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");
}
}
Engineering has 2 employees
Riya still exists after the department reference is gone
One Address shared by two Employees outlives both of them
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
}
}
Riya works in Pune
Karan works in Pune
Address still usable: Pune
Aggregation next to composition: an employee transfers, rooms belong to one house
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(); }
}
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
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
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.
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