Aggregation is a key concept in object-oriented programming (OOP) that represents a “has-a” relationship between two classes. In Java, aggregation allows one class to be a part of another class, but both classes maintain their independence.
Unlike inheritance, where one class extends another, aggregation focuses on how classes are related by ownership.
This tutorial will guide you through understanding aggregation in Java, how to implement it, and its practical use cases with examples.
Table of Contents:
1. What is Aggregation?
Aggregation is a type of association where one class contains a reference to another class. It represents a whole-part relationship, where the “whole” can exist independently of the “part.” In aggregation, one class acts as a container (or aggregator) and contains objects of another class.
For example:
A Library contains multiple Books.
A Car has Engine, Tires, and Seats.
In these cases, if the Library object is destroyed, the Books objects can still exist independently, which is what distinguishes aggregation from composition.
Key Characteristics of Aggregation:
Represents a “has-a” relationship.
The lifetime of the contained objects (parts) is independent of the container object (whole).
Both objects are related but can exist independently.
2. Difference Between Aggregation and Composition
Aggregation:
The child (part) can exist independently of the parent (whole).
Example: A Student has an Address, but an Address can exist without a Student.
Composition:
The child (part) cannot exist independently of the parent (whole).
Example: A Car has an Engine, and if the Car is destroyed, the Engine is also destroyed.
Key Difference:
In composition, the lifetime of the part depends on the whole.
In aggregation, the lifetime of the part is independent of the whole.
3. Implementing Aggregation in Java
In Java, aggregation is implemented by defining a class that holds a reference to another class as a field.
The referenced object (part) can be passed through the constructor, setter, or directly instantiated.
4. Code Example of Aggregation
Example 1: Aggregation between Student and Address
// Part class (Address) class Address { String city; String state; String country; // Constructor public Address(String city, String state, String country) { this.city = city; this.state = state; this.country = country; } public void displayAddress() { System.out.println("City: " + city); System.out.println("State: " + state); System.out.println("Country: " + country); } } // Whole class (Student) class Student { String name; int rollNo; Address address; // Aggregation (Address is part of Student) // Constructor public Student(String name, int rollNo, Address address) { this.name = name; this.rollNo = rollNo; this.address = address; // Aggregation } public void displayInfo() { System.out.println("Student Name: " + name); System.out.println("Roll No: " + rollNo); address.displayAddress(); // Calling the address object } } public class Main { public static void main(String[] args) { // Creating an Address object (can exist independently of Student) Address address = new Address("New York", "NY", "USA"); // Creating a Student object that aggregates Address Student student = new Student("John", 101, address); // Displaying Student and Address information student.displayInfo(); } }
Output:
Student Name: John Roll No: 101 City: New York State: NY Country: USA
Explanation:
Student has an Address, but both classes can exist independently.
The Address object is passed to the Student constructor, illustrating aggregation where Address can exist without Student.
5. Aggregation vs Inheritance
Aggregation:
Represents a “has-a” relationship (e.g., a Car has-a Engine).
Promotes code reuse by including existing objects within other objects.
The child object can exist independently of the parent.
Inheritance:
Represents an “is-a” relationship (e.g., a Dog is-a Animal).
Allows a class to inherit the behavior and properties of another class.
The child class is dependent on the parent class in terms of behavior.
Example 2: Aggregation vs Inheritance
// Inheritance (Animal and Dog) class Animal { public void sound() { System.out.println("Animal makes a sound."); } } class Dog extends Animal { @Override public void sound() { System.out.println("Dog barks."); } } // Aggregation (Car and Engine) class Engine { public void start() { System.out.println("Engine is starting."); } } class Car { Engine engine; // Aggregation public Car(Engine engine) { this.engine = engine; } public void startCar() { engine.start(); // Car uses Engine System.out.println("Car is ready to drive."); } } public class Main { public static void main(String[] args) { // Inheritance Example Dog dog = new Dog(); dog.sound(); // Output: Dog barks. // Aggregation Example Engine engine = new Engine(); Car car = new Car(engine); car.startCar(); // Output: Engine is starting. Car is ready to drive. } }
Explanation:
The Dog class inherits the behavior of Animal using the “is-a” relationship.
The Car class uses Engine in an aggregation relationship, as “has-a”, where Car depends on Engine but Engine can exist independently.
6. Advantages of Aggregation
Code Reusability: Aggregation allows you to reuse existing objects in new contexts, reducing code duplication.
Flexibility: Since the child (part) object can exist independently, aggregation allows greater flexibility and modular design.
Loose Coupling: Aggregation reduces coupling between classes. The “whole” class can function without being tightly bound to the “part” class.
7. Real-World Use Cases of Aggregation
1. Library and Books:
A Library contains multiple Books, but the Books can exist independently of the Library.
Example 3: Library and Book Aggregation
import java.util.List; import java.util.ArrayList; // Part class (Book) class Book { String title; String author; public Book(String title, String author) { this.title = title; this.author = author; } public void displayBookInfo() { System.out.println("Title: " + title + ", Author: " + author); } } // Whole class (Library) class Library { String name; List books; // Aggregation public Library(String name) { this.name = name; this.books = new ArrayList<>(); } public void addBook(Book book) { books.add(book); // Aggregating books in the library } public void displayLibraryInfo() { System.out.println("Library: " + name); System.out.println("Books in library:"); for (Book book : books) { book.displayBookInfo(); } } } public class Main { public static void main(String[] args) { // Creating books Book book1 = new Book("Effective Java", "Joshua Bloch"); Book book2 = new Book("Clean Code", "Robert C. Martin"); // Creating a library and adding books Library library = new Library("City Library"); library.addBook(book1); library.addBook(book2); // Displaying library information library.displayLibraryInfo(); } }
Output:
Library: City Library Books in library: Title: Effective Java, Author: Joshua Bloch Title: Clean Code, Author: Robert C. Martin
Explanation:
Library aggregates Book objects. The books can exist independently, but they are associated with the library in this example.
2. Department and Employees:
A Department has multiple Employees, but Employees can exist without the Department.
8. Conclusion
Aggregation in Java allows classes to have a “has-a” relationship, promoting code reusability and flexibility.
Unlike composition, the lifetime of the part (contained object) is independent of the whole (container object), making aggregation a looser form of association.
By using aggregation, you can structure your code to model real-world relationships effectively.
Understanding the difference between aggregation and inheritance, as well as when to use each, is essential for writing clean, maintainable, and modular Java programs.