Polymorphism
Dashboard
Topic 11/30
Home › Polymorphism

🎭 Polymorphism

Understand compile-time and runtime polymorphism, method overloading, overriding, and dynamic dispatch.

What is Polymorphism?

Polymorphism comes from Greek words meaning "many forms". In Java, it allows us to perform a single action in different ways. It is a core concept of OOP that lets you use the same interface for different underlying forms (data types).

Types of Polymorphism

  • Compile-time Polymorphism: Achieved through Method Overloading.
  • Runtime Polymorphism: Achieved through Method Overriding (Dynamic Method Dispatch).

Compile-time Polymorphism (Method Overloading)

Method overloading allows multiple methods to have the same name but different parameters (number, type, or order of arguments). The compiler determines which method to execute based on the method signature during compilation.

Warning: Changing only the return type is not sufficient to overload a method. Overloaded methods must differ in parameter lists.
MethodOverloadingDemo.javaJava
class MathOperations {
    // Adds two integers
    public int add(int a, int b) {
        return a + b;
    }

    // Adds three integers
    public int add(int a, int b, int c) {
        return a + b + c;
    }

    // Adds two doubles
    public double add(double a, double b) {
        return a + b;
    }
}

public class MethodOverloadingDemo {
    public static void main(String[] args) {
        MathOperations math = new MathOperations();
        System.out.println(math.add(10, 20));       // Calls add(int, int)
        System.out.println(math.add(10, 20, 30));   // Calls add(int, int, int)
        System.out.println(math.add(5.5, 2.5));     // Calls add(double, double)
    }
}

Runtime Polymorphism (Method Overriding)

This is a process in which a call to an overridden method is resolved at runtime rather than compile-time. This is known as Dynamic Method Dispatch. It relies on a superclass reference variable holding a subclass object (upcasting).

AnimalSound.javaJava
class Animal {
    void makeSound() {
        System.out.println("Some generic animal sound");
    }
}

class Cat extends Animal {
    @Override
    void makeSound() {
        System.out.println("Meow");
    }
}

class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("Woof");
    }
}

class Bird extends Animal {
    @Override
    void makeSound() {
        System.out.println("Chirp");
    }
}

public class AnimalSound {
    public static void main(String[] args) {
        Animal a; // Superclass reference

        a = new Cat(); // Upcasting
        a.makeSound(); // Output: Meow

        a = new Dog();
        a.makeSound(); // Output: Woof

        a = new Bird();
        a.makeSound(); // Output: Chirp
    }
}

Polymorphic Arrays

Because a superclass reference can point to subclass objects, you can create arrays of a superclass type and store any subclass object inside them. This makes processing lists of different but related objects very clean.

PolymorphicArray.javaJava
class Shape {
    void draw() { System.out.println("Drawing shape"); }
}
class Circle extends Shape {
    void draw() { System.out.println("Drawing circle"); }
}
class Rectangle extends Shape {
    void draw() { System.out.println("Drawing rectangle"); }
}

public class PolymorphicArray {
    public static void main(String[] args) {
        Shape[] shapes = new Shape[3];
        shapes[0] = new Shape();
        shapes[1] = new Circle();
        shapes[2] = new Rectangle();

        for (Shape s : shapes) {
            s.draw(); // Polymorphic call
        }
    }
}

Upcasting, Downcasting and instanceof

Upcasting is converting a subclass reference into a superclass reference (done implicitly). Downcasting is converting a superclass reference back into a subclass reference (requires explicit cast).

The instanceof operator checks whether an object is an instance of a specific class, preventing ClassCastException during downcasting.

DowncastingDemo.javaJava
class Vehicle {}
class Car extends Vehicle {
    void honk() { System.out.println("Beep beep!"); }
}

public class DowncastingDemo {
    public static void main(String[] args) {
        Vehicle v = new Car(); // Upcasting
        
        // v.honk(); // ERROR: Vehicle does not have honk()

        if (v instanceof Car) {
            Car c = (Car) v; // Downcasting
            c.honk();
        }
    }
}