Polymorphism
Polymorphism lets one piece of code work with many types, each behaving in its own way.
On this page · 5 min · Beginner
On this page
- In simple words
- Learning objectives
- Explanation
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 5 min
- Difficulty
- Beginner
- Updated
- Oct 30, 2026
Related tutorials
In simple words#
Pressing “Pay” works for UPI, card or wallet — each pays differently, but the checkout screen doesn’t care which one it has.
- Dynamic dispatch
- The real object’s method runs at runtime.
- Upcasting
- Storing a child in a parent-type variable.
- instanceof pattern
- if (p instanceof Upi u) { … } checks and casts.
Learning objectives#
- Use parent-type references
- Explain dynamic dispatch
- Distinguish overloading and overriding
- Use instanceof pattern matching
Explanation#
A variable of a parent type (or interface) can hold any child object: Payment p = new UpiPayment().
Calling p.pay() runs the child’s overridden method — decided at runtime.
Overloading is decided at compile time by parameter types; overriding at runtime by the object type.
instanceof with a pattern variable (Java 16+) checks the type and casts in one step.
Code example: One checkout, many payment types#
import java.util.List;
abstract class Payment {
abstract String pay(double amount);
}
class UpiPayment extends Payment {
String pay(double amount) { return "Paid ₹" + amount + " via UPI"; }
}
class CardPayment extends Payment {
String pay(double amount) { return "Charged ₹" + (amount + amount * 0.02) + " to card (2% fee)"; }
}
public class Checkout {
public static void main(String[] args) {
List<Payment> payments = List.of(new UpiPayment(), new CardPayment());
for (Payment p : payments) {
System.out.println(p.pay(1000)); // each object decides
if (p instanceof CardPayment card) {
System.out.println(" → card receipt for " + card.getClass().getSimpleName());
}
}
}
}
Paid ₹1000.0 via UPI Charged ₹1020.0 to card (2% fee) → card receipt for CardPayment
This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.
Step-by-step explanation#
- The list holds Payment references.
- p.pay() calls the right subclass method at runtime.
- Adding WalletPayment needs no change to Checkout.
- instanceof with a pattern variable casts safely.
Real-world use case#
Notification services send Email, SMS and WhatsApp messages through one Notifier interface.
Common mistakes#
Move behaviour into the classes and let polymorphism choose.
Overloading = same name, different params; overriding = child replaces parent method.
Best practices#
- Program to the parent type or interface
- Let objects decide their behaviour
- Use instanceof patterns sparingly
- Add new types without changing callers
Remember this#
Parent reference, child behaviour.
Override = runtime; overload = compile time.
New types plug in without changing callers.
Practice exercise#
Your task: Add a WalletPayment that returns "Paid from wallet".
Show solution
class WalletPayment extends Payment {
String pay(double amount) { return "Paid ₹" + amount + " from wallet"; }
}
Mini quiz#
When is an overridden method chosen?
Want more? Practise all Java MCQs · Java interview questions
My notes#
Saved in this browser.