IS-A and HAS-A: association, aggregation and composition
Objects relate to each other in two basic ways:
- IS-A: inheritance or implementation. A Dog IS-A Animal; an ArrayList IS-A List.
- HAS-A: one object holds a reference to another. HAS-A comes in three strengths, from loose to tight:
- Association: objects know or use each other, and each lives independently. A Doctor treats Patients; a Student attends Courses. It can be one-to-one, one-to-many or many-to-many.
- Aggregation: a whole–part association where the parts can exist on their own and may be shared. A Department has Employees; close the department and the employees still exist.
- Composition: a strong whole–part relationship. The part belongs to exactly one whole and lives and dies with it. An Order has OrderLines; delete the order and its lines are gone.
In Java all three look like fields. The difference is ownership and lifecycle: who creates the part, whether it can be shared, and what happens to it when the whole goes away.
Example
// Association: uses another object; no ownership
class Doctor {
Prescription treat(Patient patient) { return new Prescription(patient.id(), "Rest"); }
}
// Aggregation: holds parts that exist independently (created elsewhere, can move on)
class Department {
private final List<Employee> employees = new ArrayList<>();
void hire(Employee e) { employees.add(e); } // the employee existed before
void letGo(Employee e) { employees.remove(e); } // and keeps existing after
}
// Composition: creates and owns its parts; they die with it
class Order {
private final List<OrderLine> lines = new ArrayList<>();
void addItem(String sku, int qty) { lines.add(new OrderLine(sku, qty)); } // created inside
List<OrderLine> lines() { return List.copyOf(lines); } // never hand out the internals
}
record OrderLine(String sku, int qty) {}@Entity
class Order {
// Composition: lines are saved and deleted with the order
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private List<OrderLine> lines = new ArrayList<>();
}
@Entity
class Department {
// Aggregation: employees outlive the department, so no cascading delete
@OneToMany(mappedBy = "department")
private List<Employee> employees = new ArrayList<>();
}Common mistake
Calling every HAS-A relationship "composition". Ask who owns the part's lifecycle: if the part can outlive or be shared by the whole, it's aggregation.
Under the hood
In UML, association is a plain line, aggregation a hollow diamond on the whole's side and composition a filled diamond. Composition is also an encapsulation tool: because the Order owns its lines, it should never return its internal list (return a copy or an unmodifiable view), or outside code could change the order behind its back.
Check yourself
An Order creates its OrderLines, and they're deleted when the order is deleted. What relationship is this?
How this connects
Where this leads
You've reached the end of this thread. Try a learning path for what's next.
Part of OOP in practice.
Was this lesson helpful?
Finished reading? Mark it complete to track your progress.