Codectionary / Developer documentation / Java

Polymorphism

Polymorphism - literally 'many forms' - lets objects of different subclasses be treated through a common parent type or interface, while each object still behaves according to its own specific class at runtime. This is achieved through method overriding: a variable declared with a parent type can hold any subclass object, and calling an overridden method automatically runs the correct subclass version, a mechanism called dynamic (runtime) dispatch.

Syntax

ParentType variable = new ChildType();

Examples

Runtime Polymorphism with Method Overriding

The same method call produces different behavior depending on the actual object type.

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

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

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

Animal[] animals = { new Dog(), new Cat(), new Animal() };
for (Animal a : animals) {
    a.makeSound();  // calls the correct version for each actual object type
}
// Woof!
// Meow!
// Some generic sound

Polymorphism with Interfaces

Treating different implementing classes uniformly through a shared interface type.

interface Shape {
    double area();
}

class Circle implements Shape {
    private double radius;
    public Circle(double radius) { this.radius = radius; }
    public double area() { return Math.PI * radius * radius; }
}

class Square implements Shape {
    private double side;
    public Square(double side) { this.side = side; }
    public double area() { return side * side; }
}

Shape[] shapes = { new Circle(3), new Square(4) };
for (Shape s : shapes) {
    System.out.printf("Area: %.2f%n", s.area());
}

Polymorphism in Method Parameters

A method that accepts the parent type can work with any subclass, without needing to know the specific type.

class Employee {
    protected String name;
    public Employee(String name) { this.name = name; }
    public double calculatePay() { return 0; }
}

class FullTime extends Employee {
    public FullTime(String name) { super(name); }
    @Override
    public double calculatePay() { return 5000; }
}

class Contractor extends Employee {
    public Contractor(String name) { super(name); }
    @Override
    public double calculatePay() { return 3000; }
}

public class Payroll {
    public static void printPay(Employee e) {  // works with ANY Employee subclass
        System.out.println(e.name + ": $" + e.calculatePay());
    }

    public static void main(String[] args) {
        printPay(new FullTime("Fola"));
        printPay(new Contractor("Zain"));
    }
}

Best practices

  • Declare variables, parameters, and return types using the parent type or interface when you want to write flexible, extensible code
  • Rely on polymorphism instead of type-checking with instanceof and manual casting where possible - let overridden methods handle the differences
  • Remember polymorphism applies to overridden instance methods, not fields or static methods, which are resolved based on the declared type instead
  • Design parent classes/interfaces around behavior that genuinely varies by subclass, so polymorphism adds real value rather than just ceremony

At a glance

Purpose
General-purpose application development
File extension
.java
Runs in
Java Virtual Machine
Usually used with
JDK and Java libraries

Specifications & further reading

Related Java documentation

Classes
Classes in Java are blueprints for creating objects, defining their properties (fields) and behaviors (methods). Java is a strictly object-oriented language where everything is encapsulated within classes. A class serves as a template that specifies what data an object will store and what operations it can perform.
Interfaces
An interface defines a contract of methods that implementing classes must provide, without specifying how those methods work internally. Unlike a class, an interface cannot be instantiated directly - it only declares what a class can do, not how. A class uses the 'implements' keyword to fulfill an interface's contract, and a single class can implement multiple interfaces, which is Java's way of achieving a form of multiple inheritance.
Abstract Classes
An abstract class is a class that cannot be instantiated directly and may contain both fully implemented methods and abstract methods (declared without a body, which subclasses must implement). Abstract classes sit between interfaces and regular classes: like an interface, they define a contract; like a regular class, they can hold state (fields) and provide shared, already-implemented behavior.
Constructors
A constructor is a special method that runs automatically when an object is created with 'new', typically used to initialize the object's fields. A constructor shares its name with the class and has no return type, not even void. Java supports constructor overloading, letting a class offer multiple ways to construct an object, and constructor chaining with this(...), where one constructor calls another in the same class.