Polymorphism allows the same method name or interface to exhibit different behaviors depending on the object that is invoking it.
The term "polymorphism" comes from Greek and means "many forms." In programming, it allows us to write code that is generic, extensible, and reusable, while the specific behavior is determined at runtime or compile-time based on the object’s actual type.
Polymorphism lets you call the same method on different objects, and have each object respond in its own way.
You write code that targets a common type, but the actual behavior is determined by the concrete implementation.
Think of a universal remote control.
powerOn(), volumeUp(), mute().For the user, the interface (remote) never changes. But internally, each device interprets the same signal differently.
That’s polymorphism in action. The same interface triggers different behaviors depending on the receiver (device type).
Polymorphism is broadly categorized into two types based on when the method to be executed is determined.
Also known as method overloading, compile-time polymorphism occurs when:
Here, the compiler determines which version of add() to call based on the parameters before the program runs.
Also known as method overriding, this happens when:
Suppose you’re designing a system that sends notifications. You want to support email, SMS, push notifications, etc.
You start by defining a common interface.
Now, you implement it in multiple ways:
And use it like this:
You can pass any implementation of NotificationSender, and the correct behavior will be triggered based on the object passed.
This is runtime polymorphism, where the decision of which method to execute is made during execution, not at compile time.
Polymorphism is especially useful in Low-Level Design when:
For example: if you're designing a PaymentProcessor interface, you can have multiple implementations like CreditCardProcessor, PayPalProcessor, and UPIProcessor. The payment system doesn’t need to care which one it's using, it just calls processPayment().
Client code:
This system can easily be extended to support new payment types (like ApplePay or Stripe) without changing PaymentService.
Now that we have explored the four pillars of Object-Oriented Programming (Encapsulation, Abstraction, Inheritance, and Polymorphism), it’s time to look at how classes interact with one another in a real system.
Let’s begin with the most common and fundamental type of relationship: Association, where one class simply uses another to perform a specific function.