Lesson 39 of 50 – Java Polymorphism
78%

Java Polymorphism

Polymorphism is an important concept in Object-Oriented Programming (OOP). It allows the same method name or interface to represent different behaviors.

1. What Is Polymorphism?

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();
    }
}
Output:
Dog barks
Cat meows
2. Types of Polymorphism in Java
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
Remember: Overloading is resolved at compile time. Overridden instance method implementations are selected dynamically at runtime.
3. Compile-Time Polymorphism

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));
    }
}
Output:
30
60

The compiler selects the appropriate add() method based on the arguments.

4. Runtime Polymorphism

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();
    }
}
Output:
Dog barks

Although the reference type is Animal, the object is a Dog, so the overridden Dog method runs.

5. Method Overloading

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);
    }
}
Output:
Integer: 10
Text: Hello Java
Double: 15.5

Overloaded methods must differ in their parameter lists. Changing only the return type is not enough.

6. Method Overriding

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();
    }
}
Output:
Child class

The Child class overrides the inherited display() method.

7. Parent Reference and Child Object

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();
    }
}
Output:
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.

8. Polymorphism Using an Abstract Class

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());
    }
}
Output:
50.0
28.27

The same area() method call produces different results for different Shape objects.

9. Polymorphism Using an Interface

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);
    }
}
Output:
UPI payment: 500.0
Card payment: 1000.0

Both payment classes implement the same interface but perform the operation differently.

10. Polymorphism with Arrays

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();
        }
    }
}
Output:
Dog barks
Cat meows
Dog barks

Each array element refers to an Animal subclass object, and the correct overridden method runs.

11. Advantages of Polymorphism
  • Flexibility: Different object types can be used through a common parent type or interface.
  • Reusability: Common code can work with different implementations.
  • Maintainability: New implementations can often be added without changing all calling code.
  • Extensibility: Programs can support new types that follow an existing interface.
Polymorphism is useful when several classes share a common operation but need different implementations.
12. Real-Life Example: Notification System
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());
    }
}
Output:
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.

13. Practice Assignments
  1. Create a Calculator class with overloaded add() methods for two and three integers.
  2. Create Animal, Dog, and Cat classes and demonstrate method overriding.
  3. Create a Vehicle superclass and Car and Bike subclasses with overridden start() methods.
  4. Create an abstract Shape class with area() implementations for Rectangle and Circle.
  5. Create a Payment interface and implement it in UpiPayment and CardPayment classes.
  6. Create an Animal array containing Dog and Cat objects and call their sound() methods.
  7. Create a Notification interface and implement EmailNotification and SmsNotification.
Practice Tip: First write the program without polymorphism. Then introduce a parent class or interface and compare the two approaches.

🧠 Quick Quiz

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?