OOP Class Relationships: Association, Aggregation, Composition & Inheritance
This guide provides a comprehensive overview of class relationships in OOP – covering association, aggregation, composition, and inheritance with practical examples.
In a Nutshell
Class relationships describe how objects interact and work together to form complex systems. The four primary relationship types are association, aggregation, composition, and inheritance.
Technical Overview
Class relationships define how classes and their objects are connected to one another. They’re fundamental to structuring object-oriented systems.
Four main relationship types:
1. Association (has-a)
- Description: Two classes are linked to each other
- Lifecycle: Independent of one another
- Example:
CustomerhasOrders - UML: Simple line between classes
2. Aggregation (composed of)
- Description: A “has-a” relationship where parts can exist independently
- Lifecycle: Parts can exist without the whole
- Example:
CarhasTires - UML: Line with empty diamond at the whole
3. Composition (part of)
- Description: A “part-of” relationship where parts exist only with the whole
- Lifecycle: Parts cannot exist without the whole
- Example:
CarhasEngine - UML: Line with filled diamond at the whole
4. Inheritance (is-a)
- Description: An “is-a” relationship indicating specialization
- Lifecycle: Subclass inherits from superclass
- Example:
Caris aVehicle - UML: Line with empty arrow pointing to superclass
Key Concepts
- Association: Simple relationship between classes
- Aggregation: “Has-a” relationship with independent parts
- Composition: “Part-of” relationship with dependent parts
- Inheritance: “Is-a” relationship with specialization
- Multiplicity: 1, , 0..1, 1.., 0..*
- UML Notation: Various arrows and diamond symbols
- Lifecycle: Object dependency on one another
- Architectural Importance: Critical for software design
Core Components
- Association: Bidirectional or unidirectional relationships
- Aggregation: Weak “has-a” relationship
- Composition: Strong “part-of” relationship
- Inheritance: Specialization and code reuse
- Multiplicity: Number of relationship instances
- Roles: Names describing the relationship
- Navigability: Direction of the relationship
- Qualifiers: Additional relationship information
Practical Examples
1. Association (Customer – Order)
// Bidirectional association
public class Kunde {
private String kundenId;
private String name;
private List<Bestellung> bestellungen = new ArrayList<>();
public void addBestellung(Bestellung bestellung) {
bestellungen.add(bestellung);
bestellung.setKunde(this); // Backreference
}
public List<Bestellung> getBestellungen() {
return new ArrayList<>(bestellungen);
}
}
public class Bestellung {
private String bestellId;
private Date bestelldatum;
private Kunde kunde; // Backreference to customer
public void setKunde(Kunde kunde) {
this.kunde = kunde;
}
public Kunde getKunde() {
return kunde;
}
}
// Usage
Kunde meier = new Kunde("1", "Meier");
Bestellung b1 = new Bestellung("B001", new Date());
Bestellung b2 = new Bestellung("B002", new Date());
meier.addBestellung(b1);
meier.addBestellung(b2);
2. Aggregation (Car – Tires)
// Aggregation: Car has Tires, Tires can exist without Car
public class Auto {
private String modell;
private List<Reifen> reifen = new ArrayList<>();
public Auto(String modell) {
this.modell = modell;
}
public void addReifen(Reifen reifen) {
if (reifen.size() < 4) {
this.reifen.add(reifen);
}
}
public void removeReifen(Reifen reifen) {
this.reifen.remove(reifen);
// Tire continues to exist and can be assigned to another car
}
}
public class Reifen {
private String hersteller;
private int groesse;
public Reifen(String hersteller, int groesse) {
this.hersteller = hersteller;
this.groesse = groesse;
}
// Tire can exist independently from the car
public void montieren() {
System.out.println("Reifen wird montiert");
}
}
// Usage
Auto golf = new Auto("Golf");
Reifen michelin1 = new Reifen("Michelin", 195);
Reifen michelin2 = new Reifen("Michelin", 195);
golf.addReifen(michelin1);
golf.addReifen(michelin2);
// Tires can be removed and reused
golf.removeReifen(michelin1);
3. Composition (Car – Engine)
// Composition: Engine exists only with Car
public class Auto {
private String modell;
private Motor motor; // Engine cannot exist without Car
public Auto(String modell, int leistung) {
this.modell = modell;
this.motor = new Motor(leistung); // Engine created internally
}
public void starten() {
motor.starten();
System.out.println(modell + " wird gestartet");
}
public void ausschalten() {
motor.ausschalten();
System.out.println(modell + " wird ausgeschaltet");
}
// Engine is destroyed with Car
protected void finalize() {
// Engine is automatically cleaned up
}
}
public class Motor {
private int leistung;
private boolean laeuft;
// Protected constructor - only Car can create Engine
protected Motor(int leistung) {
this.leistung = leistung;
this.laeuft = false;
}
protected void starten() {
this.laeuft = true;
System.out.println("Motor mit " + leistung + " PS wird gestartet");
}
protected void ausschalten() {
this.laeuft = false;
System.out.println("Motor wird ausgeschaltet");
}
}
// Usage
Auto bmw = new Auto("BMW", 200);
bmw.starten(); // Engine starts internally
bmw.ausschalten();
// Engine cannot be created independently:
// Motor motor = new Motor(150); // Error: Constructor is protected
4. Inheritance (Vehicle – Car)
// Superclass
public abstract class Fahrzeug {
protected String marke;
protected int baujahr;
protected int aktuelleGeschwindigkeit = 0;
public Fahrzeug(String marke, int baujahr) {
this.marke = marke;
this.baujahr = baujahr;
}
// Shared methods
public void beschleunigen(int kmh) {
this.aktuelleGeschwindigkeit += kmh;
System.out.println(marke + " beschleunigt auf " + aktuelleGeschwindigkeit + " km/h");
}
public void bremsen(int kmh) {
if (aktuelleGeschwindigkeit >= kmh) {
this.aktuelleGeschwindigkeit -= kmh;
System.out.println(marke + " bremst auf " + aktuelleGeschwindigkeit + " km/h");
}
}
// Abstract method - must be implemented by subclasses
public abstract void hupen();
// Concrete method - can be overridden
public void anzeigen() {
System.out.println("Fahrzeug: " + marke + ", Baujahr: " + baujahr +
", Geschwindigkeit: " + aktuelleGeschwindigkeit + " km/h");
}
}
// Subclass
public class Auto extends Fahrzeug {
private int anzahlTueren;
private boolean klimaanlage;
public Auto(String marke, int baujahr, int anzahlTueren) {
super(marke, baujahr); // Call superclass constructor
this.anzahlTueren = anzahlTueren;
this.klimaanlage = false;
}
// Implementation of abstract method
@Override
public void hupen() {
System.out.println("Auto hupt: Tut Tut!");
}
// Additional method specific to Car
public void klimaanlageEin() {
klimaanlage = true;
System.out.println("Klimaanlage eingeschaltet");
}
// Override superclass method
@Override
public void anzeigen() {
super.anzeigen(); // Call superclass method
System.out.println(" Typ: Auto, Türen: " + anzahlTueren +
", Klimaanlage: " + (klimaanlage ? "an" : "aus"));
}
}
// Additional subclass
public class Motorrad extends Fahrzeug {
private boolean hatSeitenwagen;
public Motorrad(String marke, int baujahr, boolean hatSeitenwagen) {
super(marke, baujahr);
this.hatSeitenwagen = hatSeitenwagen;
}
@Override
public void hupen() {
System.out.println("Motorrad hupt: Iiih Iiih!");
}
public void wheelie() {
System.out.println("Motorrad macht Wheelie!");
}
@Override
public void anzeigen() {
super.anzeigen();
System.out.println(" Typ: Motorrad, Seitenwagen: " +
(hatSeitenwagen ? "ja" : "nein"));
}
}
// Usage
Fahrzeug golf = new Auto("Volkswagen", 2023, 5);
Fahrzeug harley = new Motorrad("Harley-Davidson", 2022, false);
golf.hupen(); // Auto hupt: Tut Tut!
harley.hupen(); // Motorrad hupt: Iiih Iiih!
golf.beschleunigen(50);
harley.beschleunigen(80);
golf.anzeigen();
harley.anzeigen();
// Downcasting for specific methods
if (golf instanceof Auto) {
Auto autoGolf = (Auto) golf;
autoGolf.klimaanlageEin();
}
if (harley instanceof Motorrad) {
Motorrad motorradHarley = (Motorrad) harley;
motorradHarley.wheelie();
}
UML Notation for Relationships
Association
Customer 1..* --* Order
Aggregation
Car 1 --* Tire
Composition
Car 1 --* Engine
Inheritance
Vehicle <|-- Car
Vehicle <|-- Motorcycle
Multiplicity
| Symbol | Meaning | Example |
|---|---|---|
| 1 | Exactly one | 1 Engine |
| 0..1 | Zero or one | 0..1 License |
| * | Zero or more | * Tires |
| 1..* | One or more | 1..* Doors |
| 2..4 | Between 2 and 4 | 2..4 Wheels |
Choosing the Right Relationship Type
When to use which relationship?
Use association when:
- Two classes interact but have no dependency
- The relationship is temporary or optional
- Objects exist independently of each other
Use aggregation when:
- A “has-a” relationship exists
- Parts can exist without the whole
- Parts can be shared among different wholes
Use composition when:
- A “is-part-of” relationship exists
- Parts exist only as part of the whole
- The lifecycle of the part is tied to the whole
Use inheritance when:
- An “is-a” relationship exists
- A subclass is a specialized form of the superclass
- Code reuse and polymorphism are desired
Advantages and Disadvantages
Benefits of class relationships
- Structure: Clear system architecture
- Reusability: Shared functionality across classes
- Flexibility: Easy to extend and modify
- Clarity: Models the real world accurately
- Maintainability: Targeted changes are straightforward
Drawbacks
- Complexity: Many relationships can become hard to follow
- Coupling: Strong dependencies can create problems
- Performance: Too many object connections can slow things down
- Testability: Complex relationships are harder to test
Common Exam Questions
-
What’s the difference between aggregation and composition? With aggregation, parts can exist independently; with composition, parts exist only as part of the whole.
-
Explain the multiplicity 1..*! One or more objects can be connected—at least one is required, but there’s no upper limit.
-
When do you use inheritance instead of composition? When an “is-a” relationship exists and you want to reuse code.
-
What does bidirectional association mean? Both classes know about each other and can access each other’s members.
Key Resources
- https://de.wikipedia.org/wiki/Assoziation_(UML)
- https://refactoring.guru/design-patterns/composition-over-inheritance
- https://www.uml-diagrams.org/class-diagram-relationships.html
Next in the OOP Learning Path
The next article in the OOP learning path covers OOP Inheritance: Fundamentals, Inheritance & Polymorphism — how inheritance enables code reuse.



