Java Polymorphism

What is Polymorphism?

Polymorphism in programming refers to the ability of different objects to respond to the same message or method invocation in their own unique way. It allows you to treat objects of different classes as if they were objects of a common superclass. This concept is fundamental to object-oriented programming (OOP).

Polymorphism promotes code reusability and flexibility by enabling you to write generic code that can work with a variety of different objects without needing to know their specific types.

Types of Polymorphism

Compile-time Polymorphism (Static Binding):

  • Also known as static polymorphism, it occurs during compile time, which means that the method to be called is determined at compile time.
  • This is typically achieved through method overloading, where multiple methods in the same class have the same name but differ in the number or types of their parameters.
  • Example: In a class, you may have multiple methods with the same name "calculate" but with different parameter lists, allowing you to call the appropriate method based on the arguments passed.

Run-time Polymorphism (Dynamic Binding):

  • Also known as dynamic polymorphism, it occurs at runtime, which means that the method to be called is determined during program execution.
  • This is achieved through method overriding, where a subclass provides a specific implementation for a method that is already defined in its superclass.
  • Example: You have a base class "Vehicle" with a method "startEngine," and you have subclasses like "Car" and "Motorcycle" that override this method to provide their own unique implementations. The specific method to execute depends on the object's runtime type.
Compile-time (Static) Polymorphism:
class Calculator {
    public int add(int a, int b) {
        return a + b;
    }

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

public class Main {
    public static void main(String[] args) {
        Calculator calc = new Calculator();

        int sumInt = calc.add(5, 10);          // Calls the int version of add at compile-time
        double sumDouble = calc.add(3.5, 2.7); // Calls the double version of add at compile-time

        System.out.println("Sum (int): " + sumInt);
        System.out.println("Sum (double): " + sumDouble);
    }
}

In this example, we have a Calculator class with two add methods, one accepting int parameters and the other accepting double parameters. The method to be called is determined at compile time based on the argument types. This is an example of compile-time polymorphism.

Runtime (Dynamic) Polymorphism:
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 Main {
    public static void main(String[] args) {
        Animal myAnimal = new Dog(); // Creating an object of Dog, but referencing it as an Animal

        myAnimal.makeSound(); // Calls the Dog's overridden makeSound method at runtime
    }
}

In this example, we have a class hierarchy with Animal, Dog, and Cat classes. At runtime, we create an object of Dog but reference it as an Animal. The method makeSound is called on the myAnimal object, and the decision about which makeSound method to invoke is determined at runtime based on the actual type of the object. This is an example of runtime polymorphism or dynamic binding.

Polymorphism in Java

Method Overloading:

  • Method overloading allows you to define multiple methods in the same class with the same name but different parameter lists (different number or types of parameters).
  • Java compiler determines which overloaded method to call based on the arguments provided during method invocation.
  • Example: You can have a class "Calculator" with overloaded "add" methods that accept different data types like integers, doubles, or even strings.
Example
class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
    
    public double add(double a, double b) {
        return a + b;
    }
    
    public String add(String str1, String str2) {
        return str1 + str2;
    }
}

public class Main {
    public static void main(String[] args) {
        Calculator calc = new Calculator();
        
        int sumInt = calc.add(5, 10); // Calls the int version of add
        double sumDouble = calc.add(3.5, 2.7); // Calls the double version of add
        String combinedStr = calc.add("Hello, ", "world!"); // Calls the String version of add
        
        System.out.println("Sum (int): " + sumInt);
        System.out.println("Sum (double): " + sumDouble);
        System.out.println("Combined String: " + combinedStr);
    }
}

In this example, the Calculator class has multiple add methods with different parameter types. Depending on the arguments passed, the appropriate add method is called, demonstrating method overloading.

Method Overriding:

  • Method overriding occurs when a subclass provides a specific implementation for a method that is already defined in its superclass.
  • To achieve method overriding, the subclass method must have the same name, return type, and parameter list as the superclass method.
  • Example: If you have a superclass "Shape" with a method "calculateArea," you can override it in subclasses like "Circle" or "Rectangle" to provide custom area calculation logic for each shape.
Example
class Shape {
    public double calculateArea() {
        return 0.0;
    }
}

class Circle extends Shape {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }
}

class Rectangle extends Shape {
    private double length;
    private double width;
    
    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }
    
    @Override
    public double calculateArea() {
        return length * width;
    }
}

public class Main {
    public static void main(String[] args) {
        Shape circle = new Circle(5.0);
        Shape rectangle = new Rectangle(4.0, 3.0);
        
        System.out.println("Circle Area: " + circle.calculateArea()); // Calls the Circle's overridden method
        System.out.println("Rectangle Area: " + rectangle.calculateArea()); // Calls the Rectangle's overridden method
    }
}

In this example, the Shape class has a method calculateArea, which is overridden in its subclasses Circle and Rectangle. Depending on the actual object type (runtime type), the appropriate calculateArea method is called, demonstrating method overriding.

Contact Us

Name
Email
Mobile No:
subject:
Message: