What is the difference between dynamic and static polymorphism in Java?

Learn what is the difference between dynamic and static polymorphism in java? with practical examples, diagrams, and best practices. Covers java, oop, polymorphism development techniques with visua...

Dynamic vs. Static Polymorphism in Java: A Comprehensive Guide

Hero image for What is the difference between dynamic and static polymorphism in Java?

Explore the core differences between dynamic (runtime) and static (compile-time) polymorphism in Java, understanding method overloading, overriding, and their practical implications.

Polymorphism, a cornerstone of Object-Oriented Programming (OOP), allows objects of different classes to be treated as objects of a common type. In Java, polymorphism manifests in two primary forms: static (compile-time) and dynamic (runtime). Understanding the distinction between these two is crucial for writing flexible, maintainable, and robust Java applications. This article will delve into each type, providing clear explanations and practical code examples.

Static Polymorphism (Compile-Time Polymorphism)

Static polymorphism, also known as compile-time polymorphism or early binding, is resolved during the compilation phase. The Java compiler determines which method to invoke based on the method signature (name, number, and type of parameters). The most common form of static polymorphism is method overloading.

Method Overloading

Method overloading occurs when a class has multiple methods with the same name but different parameter lists. The return type can be the same or different, but it's not part of the method signature for overloading purposes. The compiler uses the number and types of arguments passed during a method call to decide which overloaded method to execute.

class Calculator {
    // Method to add two integers
    public int add(int a, int b) {
        return a + b;
    }

    // Overloaded method to add three integers
    public int add(int a, int b, int c) {
        return a + b + c;
    }

    // Overloaded method to add two doubles
    public double add(double a, double b) {
        return a + b;
    }

    public static void main(String[] args) {
        Calculator calc = new Calculator();
        System.out.println("Sum of two integers: " + calc.add(5, 10));
        System.out.println("Sum of three integers: " + calc.add(1, 2, 3));
        System.out.println("Sum of two doubles: " + calc.add(5.5, 10.5));
    }
}

Example of method overloading in Java.

classDiagram
    class Calculator {
        +add(int a, int b): int
        +add(int a, int b, int c): int
        +add(double a, double b): double
    }
    Calculator --> main

Class diagram illustrating method overloading in the Calculator class.

Dynamic Polymorphism (Runtime Polymorphism)

Dynamic polymorphism, also known as runtime polymorphism or late binding, is resolved during the execution phase of a program. This type of polymorphism is achieved through method overriding and inheritance. When an overridden method is called, the Java Virtual Machine (JVM) determines which version of the method (from the superclass or subclass) to execute based on the actual type of the object at runtime, not the reference type.

Method Overriding

Method overriding occurs when a subclass provides a specific implementation for a method that is already defined in its superclass. The method in the subclass must have the same name, return type, and parameter list as the method in the superclass. The @Override annotation is often used to indicate that a method is intended to override a superclass method, helping to catch potential errors at compile time.

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

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

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

public class PolymorphismDemo {
    public static void main(String[] args) {
        Animal myAnimal = new Animal();
        Animal myDog = new Dog(); // Runtime polymorphism
        Animal myCat = new Cat(); // Runtime polymorphism

        myAnimal.makeSound(); // Output: Animal makes a sound
        myDog.makeSound();    // Output: Dog barks
        myCat.makeSound();    // Output: Cat meows
    }
}

Example of method overriding and runtime polymorphism.

classDiagram
    Animal <|-- Dog
    Animal <|-- Cat
    class Animal {
        +makeSound()
    }
    class Dog {
        +makeSound()
    }
    class Cat {
        +makeSound()
    }

Class diagram showing inheritance and method overriding.

Key Differences and Use Cases

The fundamental difference lies in when the method call is resolved. Static polymorphism is resolved at compile time, offering performance benefits and compile-time safety. Dynamic polymorphism is resolved at runtime, providing flexibility and extensibility, especially in scenarios involving inheritance hierarchies.

Hero image for What is the difference between dynamic and static polymorphism in Java?

Comparison of Static vs. Dynamic Polymorphism

When to Use Which?

  • Static Polymorphism (Overloading): Use when you need to perform similar operations on different data types or with varying numbers of arguments within the same class. It simplifies method naming and improves code readability.
  • Dynamic Polymorphism (Overriding): Use when you have an inheritance hierarchy and want to provide specific implementations for a common method in subclasses. It's essential for achieving extensibility and allowing new types to be handled uniformly through a common interface or superclass.