[!NOTE] The third pillar of Object-Oriented Programming is Polymorphism, which literally translates to "many forms". It allows us to perform a single overarching action, even if the underlying code is executed differently by different classes.
Method Overriding
In the previous chapter, we saw that a Lion inherits the eat() method from Animal.
But what if the generic "The animal consumes food" message isn't accurate enough? What if we want the Lion to eat specifically like a carnivore, without changing the generic Animal file?
We achieve this via Method Overriding. We write the exact same method signature in the Child class, completely overriding the Parent's implementation.
public class Animal {
public void makeSound() {
System.out.println("Generic animal noise");
}
}
public class Dog extends Animal {
// We add this annotation to tell the compiler to double check that we spelled the method right!
@Override
public void makeSound() {
System.out.println("Woof! Bark!");
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow!");
}
}
The Magic of Polymorphism
Here is where the architecture becomes incredibly powerful. Because Dog and Cat are both technically Animal objects, Java allows us to declare an Animal variable, but fill it with a completely different child object!
public class Main {
public static void main(String[] args) {
// The Box is labeled "Animal", but we put a "Dog" object inside!
Animal myPet = new Dog();
Animal myOtherPet = new Cat();
// Polymorphism in action! Java knows to call the CHILD'S overridden method!
myPet.makeSound(); // Outputs: "Woof! Bark!"
myOtherPet.makeSound(); // Outputs: "Meow!"
}
}
Real-World Use Case
Why is this useful? Imagine building a game engine containing an array of 5,000 active entities. You do not want separate arrays for Enemies, Players, NPCs, and Walls.
Using Polymorphism, you can store everything in a single GameObject[] array.
// One single loop updates thousands of completely different objects!
for(GameObject obj : allEntitiesInGame) {
// If it's a Player, they move with WASD. If an Enemy, they track you.
// The overarching loop doesn't care. It just calls the polymorphic method!
obj.updatePhysics();
}
[!TIP] Overloading vs Overriding: Overloading is two methods in the SAME class with DIFFERENT parameters. Overriding is two methods in parent/child classes with the EXACT SAME parameters.