Polymorphism
Overview
Polymorphism translates from Greek as 'Many Forms'. In Java, it is the ability of an object to take on many forms. Specifically, it allows a Parent reference variable to hold a Child object.
Because a Dog extends Animal, a Dog IS-A Animal. Therefore, you can safely write: Animal a = new Dog();. The variable a is of type Animal, but the actual object living in Heap memory is a Dog.
This is incredibly powerful for writing highly dynamic, reusable code. You can write a single method processPayment(PaymentMethod p) that accepts any child of PaymentMethod (CreditCard, PayPal, Crypto). You don't need to write three different methods. When you call p.pay(), the JVM intelligently executes the specific code of whichever object was actually passed in (Dynamic Method Dispatch).
Syntax
class Animal {
public void makeSound() { System.out.println("Some generic sound"); }
}
class Dog extends Animal {
@Override
public void makeSound() { System.out.println("Bark!"); }
}
class Cat extends Animal {
@Override
public void makeSound() { System.out.println("Meow!"); }
}
public class Main {
public static void main(String[] args) {
// Polymorphism in action: Parent reference, Child object!
Animal myPet = new Dog();
// The compiler sees 'Animal', but at runtime,
// the JVM knows it's actually a Dog!
myPet.makeSound(); // Prints "Bark!"
// Re-assigning to a different form
myPet = new Cat();
myPet.makeSound(); // Prints "Meow!"
// Powerful usage: Polymorphic Arrays!
Animal[] zoo = { new Dog(), new Cat(), new Animal() };
for(Animal a : zoo) {
a.makeSound(); // Polymorphically calls the right method!
}
}
}Common Pitfalls
- The Reference Type limits visibility. If the
Dogclass has a special methodfetch(), you CANNOT callmyPet.fetch()ifmyPetis declared as anAnimal. The compiler only looks at the Reference Type (Animal) to see what methods are allowed. You would have to manually downcast it:((Dog) myPet).fetch();. - Dangerous Downcasting resulting in ClassCastException. If you do
Animal a = new Cat();and then blindly force a downcastDog d = (Dog) a;, the application will crash at runtime because a Cat is NOT a Dog. Always check first using theinstanceofkeyword.
Interview Questions
Compile-time polymorphism is achieved through Method Overloading. The compiler knows exactly which method to call based on the arguments provided. Runtime polymorphism is achieved through Method Overriding (Dynamic Method Dispatch). The compiler doesn't know which overridden method will execute; the JVM determines it dynamically at runtime based on the actual object in memory.
No! Polymorphism (Dynamic Dispatch) ONLY applies to overridden methods. Variables are not polymorphic. If a Parent and Child both declare an int age variable, accessing parentRef.age will ALWAYS return the Parent's variable, regardless of what object it points to.
Real-World Example
A standard Notification system uses Polymorphism to send alerts across multiple channels effortlessly without duplicating the core logic loops.
public void sendBulkAlerts(List<Notifier> notifiers, String msg) {
// We don't care if it's an EmailNotifier, SMSNotifier, or PushNotifier.
// We just know they all share the Parent 'Notifier' contract!
for(Notifier n : notifiers) {
n.send(msg); // Dynamic Dispatch handles the rest
}
}Check Your Knowledge
Test your understanding of Polymorphism with these quick questions.