Java OOP Study Notes
Polymorphism · Final Keyword · Aggregation & Composition
1. Polymorphism
Polymorphism is derived from two Greek words: poly (many) and morphs (forms). So it means
many forms.
• A subclass has its own behaviour — a parent class cannot have the behaviour of its
subclass.
• Method Overloading is compile-time (false/static) polymorphism.
• Method Overriding is runtime (true/dynamic) polymorphism.
1.1 Basic Polymorphism (Tight Coupling)
Each subclass reference calls its own overridden fly() method:
class Plane { void fly() { [Link]("Plane is flying"); } }
class CargoPlane extends Plane {
void fly() { [Link]("CargoPlane is flying at low height"); }
}
class PassengerPlane extends Plane {
void fly() { [Link]("PassengerPlane is flying at medium height"); }
}
class FighterPlane extends Plane {
void fly() { [Link]("FighterPlane is flying at great height"); }
}
// In main:
CargoPlane cp = new CargoPlane();
[Link](); // CargoPlane is flying at low height
1.2 Polymorphism with Parent-type Reference (Loose Coupling)
A parent-type reference (Plane ref) can hold any child object. When [Link]() is called, the child's
version runs — this is runtime polymorphism:
Plane ref;
ref = cp; [Link](); // CargoPlane is flying at low height
ref = pp; [Link](); // PassengerPlane is flying at medium height
ref = fp; [Link](); // FighterPlane is flying at great height
📝 Note: Creating parent-type references to child objects is called loose coupling, through
which polymorphism is achieved.
📝 Note: Using a parent-type reference, you can invoke ONLY inherited/overridden methods
— NOT specialized child methods.
1.3 Accessing Specialized Methods — Downcasting
Trying to call carryCargo() via a Plane ref causes a compile error. You must downcast:
// ERROR: [Link](); // Plane doesn't know this method
// CORRECT — downcast to child type:
((CargoPlane)ref).carryCargo();
((PassengerPlane)ref).carryPassenger();
((FighterPlane)ref).carryWeapons();
• Downcasting explicitly converts a parent-type reference to a child-type reference.
• Use with caution — causes ClassCastException at runtime if the object is not actually
the target child type.
1.4 Achieving Polymorphism Advantages with Airport Example
Instead of calling fly/takeOff/land three times in main, extract a permit() method in Airport:
class Airport {
void permit(Plane ref) { // accepts ANY Plane subclass
[Link]();
[Link]();
[Link]();
}
}
// In main:
Airport a = new Airport();
[Link](cp); [Link](pp); [Link](fp);
• Code reusability — one permit() handles all plane types.
• Flexibility — add new plane types without changing Airport.
• Reduction in complexity — main is cleaner and shorter.
1.5 Rules of Method Overriding
• Name and return type of the method cannot be changed.
• Access modifier can only be widened: default → protected → public.
• Static methods cannot be overridden — redefining in a child class is called method
hiding.
• Final methods cannot be overridden.
2. Final Keyword
The final keyword in Java is used to restrict the user. It can be applied in three contexts:
1. Final Variable
2. Final Method
3. Final Class
2.1 Final Variable
A final variable acts as a constant — its value cannot be changed after initialization. An
uninitialized final variable can only be set in the constructor.
class Test {
final int a = 100;
}
// t1.a = 200; // ERROR: cannot assign a value to final variable a
2.2 Final Method
A final method cannot be overridden by any subclass:
class Test1 {
final void fun() { [Link]("Inside parent class method"); }
}
class Test2 extends Test1 {
// void fun() { ... } // ERROR: fun() in Test2 cannot override fun() in Test1
}
2.3 Final Class
A final class cannot be extended (inherited) by any other class:
final class Test1 { ... }
// class Test2 extends Test1 { } // ERROR: cannot inherit from final Test1
3. Aggregation and Composition
Aggregation and Composition are two types of has-a relationships in OOP (distinct from is-a
which is handled by inheritance).
Property Aggregation (Has-A loose) Composition (Has-A tight)
Binding Loosely bound Tightly bound
Independent? Yes — child can exist without No — child cannot exist without
parent parent
Real Example Mobile ↔ Charger (charger works Mobile ↔ OS (OS can't run without
without phone) mobile)
3.1 Aggregation Example (Mobile + Charger)
class Charger {
private String brand;
private float voltage;
public Charger(String brand, float voltage) { ... }
public String getBrand() { return brand; }
public float getVoltage() { return voltage; }
}
class Mobile {
OS os = new OS("Android", 512); // Composition
void hasA(Charger c) { // Aggregation
[Link]([Link]());
[Link]([Link]());
}
}
// In main:
Charger c = new Charger("Samsung", 24.5f);
Mobile m = new Mobile();
[Link](c); // Aggregation: Charger passed in, can outlive Mobile
m = null; // Mobile is gone, but Charger c still exists
3.2 Composition Example (Student + Heart/Brain)
Student creates Heart and Brain inside itself — they cannot exist without Student:
class Student {
Heart h = new Heart(289, 72); // Composition (tight)
Brain b = new Brain(1400, "grey"); // Composition (tight)
void hasA(Book book) { ... } // Aggregation (loose)
void hasA(Bike bike) { ... } // Aggregation (loose)
}
• Heart and Brain are created inside Student — they die when Student is set to null.
• Book and Bike are passed into Student methods — they survive after Student is nullified.
3.3 Relationship Summary
Relationship Type Handled By
Is-A Inheritance extends keyword
Has-A (loose) Aggregation Method parameter / field
Has-A (tight) Composition Object created inside class
TAP Academy — Java OOP Notes (Pages 124–143)