Polymorphism is an important concept in Object-Oriented Programming (OOP). It allows the same method name or interface to represent different behaviors.
The word polymorphism means "many forms." In Java, polymorphism allows one operation to behave differently depending on the method arguments or the actual object.
For example, different animals make different sounds.
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
@Override
void sound() {
System.out.println("Cat meows");
}
}
class Main {
public static void main(String[] args) {
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.sound();
a2.sound();
}
}
Dog barks
Cat meows
| Type | How It Works | Example |
|---|---|---|
| Compile-time polymorphism | The compiler selects an overloaded method. | Method overloading |
| Runtime polymorphism | An overridden instance method is selected based on the actual object. | Method overriding |
Compile-time polymorphism is commonly demonstrated using method overloading. Methods share a name but have different parameter lists.
class Calculator {
int add(int a, int b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
public static void main(String[] args) {
Calculator c1 = new Calculator();
System.out.println(c1.add(10, 20));
System.out.println(c1.add(10, 20, 30));
}
}
30
60
The compiler selects the appropriate
add() method based on the arguments.
Runtime polymorphism occurs when a subclass overrides an inherited instance method and Java selects the implementation based on the actual object's class.
class Animal {
void sound() {
System.out.println("Animal sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks");
}
}
class Main {
public static void main(String[] args) {
Animal a1 = new Dog();
a1.sound();
}
}
Dog barks
Although the reference type is
Animal, the object is a
Dog, so the overridden Dog method runs.
Method overloading means defining methods with the same name but different parameter lists.
class Display {
void show(int number) {
System.out.println("Integer: " + number);
}
void show(String text) {
System.out.println("Text: " + text);
}
void show(double number) {
System.out.println("Double: " + number);
}
public static void main(String[] args) {
Display d1 = new Display();
d1.show(10);
d1.show("Hello Java");
d1.show(15.5);
}
}
Integer: 10
Text: Hello Java
Double: 15.5
Overloaded methods must differ in their parameter lists. Changing only the return type is not enough.
Method overriding allows a subclass to provide a different implementation of an inherited instance method with a compatible signature.
class Parent {
void display() {
System.out.println("Parent class");
}
}
class Child extends Parent {
@Override
void display() {
System.out.println("Child class");
}
public static void main(String[] args) {
Child c1 = new Child();
c1.display();
}
}
Child class
The Child class overrides the inherited
display() method.
A superclass reference can refer to an object of its subclass. This is one of the key features used in runtime polymorphism.
class Vehicle {
void start() {
System.out.println("Vehicle starts");
}
}
class Car extends Vehicle {
@Override
void start() {
System.out.println("Car starts with a key");
}
}
class Bike extends Vehicle {
@Override
void start() {
System.out.println("Bike starts with a button");
}
}
class Main {
public static void main(String[] args) {
Vehicle v1 = new Car();
Vehicle v2 = new Bike();
v1.start();
v2.start();
}
}
Car starts with a key
Bike starts with a button
The reference type determines which members are accessible at compile time. The actual object determines which overridden instance method runs.
An abstract class can define an abstract method that subclasses must implement, unless the subclass is also abstract.
abstract class Shape {
abstract double area();
}
class Rectangle extends Shape {
double length;
double width;
Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override
double area() {
return length * width;
}
}
class Circle extends Shape {
double radius;
Circle(double radius) {
this.radius = radius;
}
@Override
double area() {
return Math.PI * radius * radius;
}
}
class Main {
public static void main(String[] args) {
Shape s1 = new Rectangle(10, 5);
Shape s2 = new Circle(3);
System.out.println(s1.area());
System.out.printf("%.2f%n", s2.area());
}
}
50.0
28.27
The same area() method call produces
different results for different Shape objects.
An interface defines a contract that implementing classes provide. Different implementations can be accessed through the same interface reference.
interface Payment {
void pay(double amount);
}
class UpiPayment implements Payment {
@Override
public void pay(double amount) {
System.out.println("UPI payment: " + amount);
}
}
class CardPayment implements Payment {
@Override
public void pay(double amount) {
System.out.println("Card payment: " + amount);
}
}
class Main {
public static void main(String[] args) {
Payment p1 = new UpiPayment();
Payment p2 = new CardPayment();
p1.pay(500);
p2.pay(1000);
}
}
UPI payment: 500.0
Card payment: 1000.0
Both payment classes implement the same interface but perform the operation differently.
A superclass array can hold references to objects of different subclasses.
class Animal {
void sound() {
System.out.println("Animal sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
@Override
void sound() {
System.out.println("Cat meows");
}
}
class Main {
public static void main(String[] args) {
Animal[] animals = {
new Dog(),
new Cat(),
new Dog()
};
for (Animal animal : animals) {
animal.sound();
}
}
}
Dog barks
Cat meows
Dog barks
Each array element refers to an Animal subclass object, and the correct overridden method runs.
interface Notification {
void send(String message);
}
class EmailNotification implements Notification {
@Override
public void send(String message) {
System.out.println("Email: " + message);
}
}
class SmsNotification implements Notification {
@Override
public void send(String message) {
System.out.println("SMS: " + message);
}
}
class Main {
static void notifyUser(Notification notification) {
notification.send("Welcome to Java!");
}
public static void main(String[] args) {
notifyUser(new EmailNotification());
notifyUser(new SmsNotification());
}
}
Email: Welcome to Java!
SMS: Welcome to Java!
The notifyUser() method works with
any object implementing Notification. Each
implementation decides how to send the message.
add() methods for two and three integers.
start() methods.
area() implementations for Rectangle
and Circle.
sound() methods.
1. What does polymorphism mean?
2. Which feature is commonly used to demonstrate runtime polymorphism?
3. Can an interface reference refer to an implementing class object?