Java Polymorphism

Java polymorphism means that one type can represent objects of many related classes. It lets code work with a general parent type while Java runs the correct child class behavior.

Polymorphism is commonly used with inheritance and method overriding. A variable can have the type of a superclass, but hold an object from a subclass.

Polymorphism With Inheritance

In this example, Animal is the parent class. Dog and Cat both override the speak() method, so each animal can speak in its own way.

class Animal {
    void speak() {
        System.out.println("The animal makes a sound.");
    }
}

class Dog extends Animal {
    @Override
    void speak() {
        System.out.println("The dog says woof.");
    }
}

class Cat extends Animal {
    @Override
    void speak() {
        System.out.println("The cat says meow.");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal first = new Dog();
        Animal second = new Cat();

        first.speak();
        second.speak();
    }
}

Output:

The dog says woof.
The cat says meow.

The variables first and second both have the type Animal. At runtime, Java looks at the actual object: the first object is a Dog, and the second object is a Cat. That is why the overridden methods run.

Why This Is Useful

Polymorphism lets you write code that focuses on what objects have in common. If every subclass provides the same method, other code can call that method without checking the exact class first.

A common pattern is to store different subclass objects in an array or list of the parent type.

class Shape {
    double area() {
        return 0.0;
    }
}

class Rectangle extends Shape {
    private double width;
    private double height;

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

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

class Circle extends Shape {
    private double radius;

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

    @Override
    double area() {
        return 3.14 * radius * radius;
    }
}

public class Main {
    public static void main(String[] args) {
        Shape[] shapes = {
            new Rectangle(4.0, 5.0),
            new Circle(3.0)
        };

        for (Shape shape : shapes) {
            System.out.println(shape.area());
        }
    }
}

Output:

20.0
28.259999999999998

The loop only knows that each item is a Shape. It calls area() on each one, and Java chooses the version from Rectangle or Circle.

Reference Type And Object Type

With polymorphism, there are two types to think about:

  • The reference type is the variable type, such as Shape.
  • The object type is the real class created with new, such as Rectangle.

The reference type controls which methods you are allowed to call. The object type controls which overridden version runs. For example, a Shape reference can call area(), but it cannot call a method that exists only in Rectangle unless you cast it carefully.

Polymorphism With Interfaces

Polymorphism also works with interfaces. If several classes implement the same interface, code can use the interface type and call its methods.

For example, a Payable interface could be implemented by Invoice and Employee. A payroll program could store both as Payable values and call getPaymentAmount() on each one.

Common Mistakes

  • Forgetting to override the parent method with the same name, return type, and parameters.
  • Expecting Java to run a child-only method through a parent reference.
  • Thinking polymorphism changes the object. It changes how you refer to and use the object.
  • Using inheritance only to get polymorphism when an interface would express the shared behavior more clearly.

Takeaway: polymorphism lets Java code use a common parent or interface type while each object still runs its own overridden behavior.