Runtime Polymorphism
Why does Java call the child's method even when the variable type is the parent? Explore the mechanics of Method Overriding and Runtime Polymorphism.
In the first part of our Polymorphism deep-dive, we looked at how a parent variable can store a child object. Now, we're going to tackle the most powerful, and often the most confusing, aspect of OOP: Method Overriding.
The Essentials
The "Specialized Behavior" guide to this post:
- Overriding: When a child class replaces a parent's generic behavior with its own specific logic.
- Dynamic Binding: The computer chooses which method to call at Runtime based on the real object in memory.
- Signature Rules: To override, you must match the parent's method name and parameters exactly.
- Variable Shadowing: Attributes are not polymorphic; only methods are.
What is Method Overriding?
Overriding occurs when a child class provides its own specialized version of a method that is already defined in its parent class.
Think of it as generic vs. specialized.
- A parent class
Animalmight have a methodwalk()that prints a simple "Walking..." message. - A
Dogis anAnimal, so it must be able to walk. However, a Dog might want to walk in a specific way (e.g., "Dog is walking...").
By Overriding the walk() method, the Dog class replaces the generic behavior with its own specialized logic.
The Golden Rules of Overriding
For a method to be considered an override, it must meet two strict criteria:
- Same Signature: It must have the exact same name and the exact same parameters.
- Same Return Type: The return type must match exactly.
Java Implementation
class Animal {
void walk() { System.out.println("Walking..."); }
}
class Dog extends Animal {
@Override
void walk() { System.out.println("Dog is walking..."); }
}Runtime Polymorphism in Action: The Binding Mystery
The compiler only looks at the variable type. If the Animal remote doesn't have a bark() button, you can't press it.
a.bark(); // ERROR!
// Animal doesn't know "bark"
The "Remote" determines what you are ALLOWED to do.
When you press a button that exists on both (like walk), the computer calls the specialized version of the real object.
a.walk(); // "Dog is walking"
// Decision made at Runtime!
The "Real Object" in memory determines HOW the action is performed.
Code Implementation: Specialized Behavior
Here is how you override methods to provide specific child logic while using a parent reference:
class User {
login() { console.log("User logging in..."); }
}
class Student extends User {
// Override: Providing specialized logic
login() { console.log("Student logging in via OAuth..."); }
}
class Mentor extends User {
login() { console.log("Mentor logging in via Admin Portal..."); }
}
// Treat them all as generic Users
const users: User[] = [new Student(), new Mentor()];
users.forEach(u => u.login());
// Output: "OAuth...", then "Admin Portal..."Why "Student s = new User()" is Illegal
A common question is: "If I can store a Student in a User variable, can I do the reverse?"
Student s = new User(); // COMPILE-TIME ERRORThe answer is a hard No.
- Logical Failure: Not every User is a Student. A User could be a Mentor.
- Practical Failure: If the compiler allowed this, you might try to call
s.psp. But a genericUserobject doesn't have apspattribute. The code would crash immediately.
Practice what you just read.