Skip to content

7.3. Polymorphism in Java

Polymorphism means "many forms". In Java, it allows objects of different classes to be treated as objects of a common superclass. There are two types:

  • Compile-time polymorphism (Method overloading)
  • Runtime polymorphism (Method overriding)

Runtime Polymorphism Example

java
class Animal {
    public void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

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

    public void fetch() {
        System.out.println("Dog fetches ball");
    }
}

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

    public void scratch() {
        System.out.println("Cat scratches furniture");
    }
}

public class PolymorphismDemo {
    public static void main(String[] args) {
        // Polymorphic behavior
        Animal myAnimal = new Dog();  // Animal reference, Dog object
        myAnimal.makeSound();         // Output: Dog barks (runtime decision)

        myAnimal = new Cat();         // Same reference, different object
        myAnimal.makeSound();         // Output: Cat meows (runtime decision)

        // Cannot access subclass-specific methods
        // myAnimal.fetch();  // Compilation error
    }
}

Polymorphism with Collections

java
import java.util.ArrayList;
import java.util.List;

class Shape {
    public void draw() {
        System.out.println("Drawing a shape");
    }

    public double getArea() {
        return 0.0;
    }
}

class Circle extends Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public void draw() {
        System.out.println("Drawing a circle with radius " + radius);
    }

    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

class Rectangle extends Shape {
    private double width, height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public void draw() {
        System.out.println("Drawing a rectangle " + width + "x" + height);
    }

    @Override
    public double getArea() {
        return width * height;
    }
}

public class ShapeDemo {
    public static void main(String[] args) {
        List<Shape> shapes = new ArrayList<>();
        shapes.add(new Circle(5.0));
        shapes.add(new Rectangle(4.0, 6.0));
        shapes.add(new Circle(3.0));

        // Polymorphic method calls
        for (Shape shape : shapes) {
            shape.draw();           // Runtime polymorphism
            System.out.println("Area: " + shape.getArea());
            System.out.println("---");
        }
    }
}

Polymorphism in Method Parameters

java
class Employee {
    protected String name;

    public Employee(String name) {
        this.name = name;
    }

    public void work() {
        System.out.println(name + " is working");
    }
}

class Manager extends Employee {
    public Manager(String name) {
        super(name);
    }

    @Override
    public void work() {
        System.out.println(name + " is managing the team");
    }
}

class Developer extends Employee {
    public Developer(String name) {
        super(name);
    }

    @Override
    public void work() {
        System.out.println(name + " is writing code");
    }
}

class Company {
    // Method accepts any Employee subclass
    public static void assignWork(Employee employee) {
        employee.work();  // Polymorphic call
    }

    public static void main(String[] args) {
        Employee emp1 = new Manager("Alice");
        Employee emp2 = new Developer("Bob");

        assignWork(emp1);  // Output: Alice is managing the team
        assignWork(emp2);  // Output: Bob is writing code
    }
}

The instanceof Operator

java
public class InstanceOfDemo {
    public static void main(String[] args) {
        Animal animal = new Dog();

        // Check object type at runtime
        if (animal instanceof Dog) {
            System.out.println("It's a dog!");
            Dog dog = (Dog) animal;  // Safe casting
            dog.fetch();
        }

        if (animal instanceof Animal) {
            System.out.println("It's an animal!");
        }

        // Pattern matching (Java 14+)
        if (animal instanceof Dog d) {
            d.fetch();  // No explicit casting needed
        }
    }
}

Polymorphism Benefits

  1. Code Flexibility: Easily extendable
  2. Maintainability: Changes affect multiple types
  3. Reusability: Common interfaces for different types
  4. Simplified Code: Single interface for multiple implementations