Skip to main content
Advertisement

7.3 Polymorphism

Polymorphism is one of the most powerful features of object-oriented programming. It literally means "many forms." In Java, it’s the ability of a single reference variable to refer to objects of different types and perform essentially the same action in different ways. Polymorphism makes code flexible and robust to changes.

To grasp polymorphism in Java, you need to understand two key concepts:

  1. Method Overriding: Redefining a parent class's method to suit the child class.
  2. Reference Type Casting (Upcasting/Downcasting): Using a parent-type variable to point to a child-type object.

1. Method Overriding

As established in Inheritance, a child inherits its parent's methods. But what if the inherited behavior doesn't quite fit the child? In that case, the child can redefine the method's behavior to suit itself. This is called overriding.

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

class Dog extends Animal {
// Redefining (overriding) the parent's cry() method
@Override
void cry() {
System.out.println("Woof woof!");
}
}

class Cat extends Animal {
// Redefining (overriding) the parent's cry() method
@Override
void cry() {
System.out.println("Meow~");
}
}

Rules of Overriding

  1. The method name, parameters, and return type must be exactly identical to those of the parent class.
  2. The access modifier of the overriding method cannot be more restrictive than that of the parent's method. (e.g., if the parent's method is public, the child cannot restrict it to private).
  3. Adding the @Override annotation is highly recommended. It tells the compiler to check if you actually overrode a method correctly (preventing typo errors).

2. Upcasting, Downcasting, and the Magic of Polymorphism

The true magic of polymorphism appears when a variable of a parent class type holds a reference to an object of a child class type.

// A parent remote control (reference variable) can control a child TV (object)!
Animal myDog = new Dog(); // A parent type reference variable pointing to a child object
Animal myCat = new Cat();

Why does this code work without errors? Because a Dog is an Animal, and a Cat is an Animal (the "IS-A" relationship is valid). This automatic type conversion is called Upcasting and can be done implicitly.

Conversely, "An Animal is a Dog" is not always true. Therefore, to assign a parent object reference back to a child variable, you must explicitly specify the type using parentheses (Type). This is known as Downcasting.

Flexible Code with Polymorphism

Why is polymorphism so beneficial? Because it allows you to bundle different types of objects into a single array or unify method parameters.

public class PolymorphismExample {
public static void main(String[] args) {
// Managing various child objects at once using an array of the parent type
Animal[] animals = new Animal[3];
animals[0] = new Dog();
animals[1] = new Cat();
animals[2] = new Animal();

// Iterating and calling cry() on each
for (Animal a : animals) {
a.cry(); // The output changes based on the actual object type (Dynamic Binding)
}
}
}

[Execution Result]

Woof woof!
Meow~
The animal makes a sound.

We placed both Dogs and Cats into an Animal type array! And we issued the identical commanda.cry(). However, depending on whether it points to a Dog or a Cat, it produces a ** different result**because the child classes overrode the method.

Without polymorphism and the parent class, you would have needed separate arrays and loops: one for Dogs, and one for Cats. If a new animal Cow is added later, you simply drop it into the animals array without rewriting existing loops or logic. This is the profound power of polymorphism!

Advertisement