BhauAutomation

Java Polymorphism

Polymorphism in Java allows an object to take many forms. It enables a single interface to represent different data types or behaviors, improving flexibility and code reusability. Polymorphism is one of the four fundamental OOP concepts (along with Encapsulation, Inheritance, and Abstraction).

📘 Topic: Core Java / OOPs
Read time: 7 min
📊 Level: Intermediate
🔄 Focus: Many Forms
📖 Overview

What is Polymorphism in Java?

Polymorphism means "many forms." In Java, it refers to the ability of a single function, operator, or object to behave differently based on the context. It's one of the key principles of Object-Oriented Programming (OOPs).

Real-world example: A person can be a father, a husband, an employee — all at the same time (many forms). Similarly, in Java, a single method can have multiple implementations (method overriding) or multiple methods can share the same name (method overloading).

📂 Types

Types of Polymorphism in Java

⚙️ Compile-time Polymorphism (Static Binding)

Achieved by method overloading. Allows multiple methods with the same name but different parameters (different number, types, or order) in a class. Resolved at compile-time.

🔄 Runtime Polymorphism (Dynamic Binding)

Achieved by method overriding. The call to an overridden method is resolved at runtime, allowing dynamic behavior based on the actual object type.

🎯 Objectives

Objectives of Polymorphism

🔹 Code Flexibility: The main objective of polymorphism is to enhance code flexibility and reusability.

🔹 Dynamic Method Execution: It supports method overriding and overloading in classes, enabling dynamic method execution at runtime.

🔹 Code Reusability: Reduces code duplication by allowing multiple implementations under a common interface.

✅ Advantages

Advantages of Polymorphism

🔧 Maintainability: Polymorphism improves code maintainability by allowing a single interface to handle different data types.

🔄 Dynamic Behavior: It supports dynamic behavior in applications (runtime polymorphism with method overriding).

🔌 Loose Coupling: Encourages loose coupling between classes, making the code easier to extend and manage.

📦 Code Reusability: Promotes code reuse by allowing developers to write more generic and flexible code.

⚖️ Comparison

Compile-time vs Runtime Polymorphism

Aspect Compile-time Polymorphism Runtime Polymorphism
Also known as Static Binding / Early Binding Dynamic Binding / Late Binding
Achieved by Method Overloading Method Overriding
When resolved At compile-time At runtime
Speed Faster (no runtime overhead) Slightly slower (method lookup at runtime)
Keyword used Method name reuse @Override annotation
💻 Example 1

Method Overriding (Runtime Polymorphism)

// Parent class
class Animal {
    void sound() {
        System.out.println("Animal makes a sound");
    }
}

// Child class
class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

// Another child class
class Cat extends Animal {
    @Override
    void sound() {
        System.out.println("Cat meows");
    }
}

// Main class demonstrating runtime polymorphism
public class RuntimePolymorphismExample {
    public static void main(String[] args) {
        Animal myAnimal;  // reference variable of Animal
        
        myAnimal = new Dog();
        myAnimal.sound();  // Output: Dog barks
        
        myAnimal = new Cat();
        myAnimal.sound();  // Output: Cat meows
    }
}
💻 Example 2

Method Overloading (Compile-time Polymorphism)

class Calculator {
    // Method with two int parameters
    int add(int a, int b) {
        return a + b;
    }
    
    // Overloaded method with three int parameters
    int add(int a, int b, int c) {
        return a + b + c;
    }
    
    // Overloaded method with double parameters
    double add(double a, double b) {
        return a + b;
    }
    
    // Overloaded method with different order of parameters
    int add(int a, double b) {
        return a + (int)b;
    }
}

public class CompileTimePolymorphismExample {
    public static void main(String[] args) {
        Calculator calc = new Calculator();
        
        System.out.println("Sum (2 ints): " + calc.add(5, 10));
        System.out.println("Sum (3 ints): " + calc.add(5, 10, 15));
        System.out.println("Sum (2 doubles): " + calc.add(5.5, 3.2));
        System.out.println("Sum (int, double): " + calc.add(5, 3.2));
    }
}
💻 Example 3

Polymorphism Using Interfaces

interface Drawable {
    void draw();
}

class Circle implements Drawable {
    public void draw() {
        System.out.println("Drawing Circle");
    }
}

class Rectangle implements Drawable {
    public void draw() {
        System.out.println("Drawing Rectangle");
    }
}

public class InterfacePolymorphismExample {
    public static void main(String[] args) {
        Drawable d;  // Interface reference
        
        d = new Circle();
        d.draw();     // Output: Drawing Circle
        
        d = new Rectangle();
        d.draw();     // Output: Drawing Rectangle
    }
}
🏆 Best Practices

Best Practices for Polymorphism

Use polymorphism to write more generic and reusable code

Always use the @Override annotation when overriding methods

Prefer method overriding (runtime polymorphism) for extensible frameworks

Use method overloading for convenience methods (e.g., multiple parameter options)

Follow consistent naming conventions for overloaded methods

Avoid using method overloading with the same number of parameters but different return types (compiler error)