☕ Java

Polymorphism in Java

Polymorphism means 'many forms'. The same method call can behave differently depending on which object is actually behind it. It's what lets you write flexible, extensible code without rewriting existing logic.

Two Types of Polymorphism

Java has two distinct forms: 1. Compile-time Polymorphism — Method Overloading. Same method name, different parameter lists. Java decides which version to call at compile time based on the arguments you pass. 2. Runtime Polymorphism — Method Overriding. A parent reference holds a child object. Java decides which implementation to call at runtime, based on the actual object type. The second one is more powerful and more commonly what people mean when they say "polymorphism".

Method Overloading — Same Name, Different Signatures

Real-life analogy: Think of a "print" button. Press it on a document → prints the document. Press it on a photo → prints the photo. Press it and specify a range → prints those pages. Same button, different behavior based on what you give it.
Java
class Calculator {

    // Three methods with the same name — different parameter types
    public int add(int a, int b) {
        return a + b;
    }

    public double add(double a, double b) {
        return a + b;
    }

    public int add(int a, int b, int c) {
        return a + b + c;
    }
}

Calculator calc = new Calculator();
System.out.println(calc.add(2, 3));        // 5   — calls the int version
System.out.println(calc.add(2.5, 3.5));   // 6.0 — calls the double version
System.out.println(calc.add(1, 2, 3));    // 6   — calls the 3-param version

Runtime Polymorphism — One Interface, Many Behaviors

This is where it gets powerful. You can store a Dog or Cat object in an Animal variable. When you call .sound(), Java figures out at runtime which actual class the object is, and calls the right version. This is called dynamic dispatch. Real-world use: Imagine a payment system. You have a PaymentMethod interface. CreditCard, UPI, and NetBanking all implement it. Your checkout code just calls payment.process() — it doesn't care which method it is. You can add a new payment type without touching the checkout logic.
Java
// Store different subclass objects in a parent-type reference
Animal a1 = new Dog();
Animal a2 = new Cat();

a1.sound();  // Woof! — Java calls Dog's sound() at runtime
a2.sound();  // Meow! — Java calls Cat's sound() at runtime

// The real power: processing a mixed list uniformly
Animal[] zoo = { new Dog(), new Cat(), new Dog(), new Cat() };
for (Animal animal : zoo) {
    animal.sound();  // Each calls its own version — no if/else needed!
}