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.
Compile-time Polymorphism (Static Binding):
Run-time Polymorphism (Dynamic Binding):
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.
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.
Method Overloading:
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:
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.