Polymorphism in Java
Polymorphism is one of the fundamental concepts of Object-Oriented Programming (OOP). In Java, polymorphism allows an object to take on many forms. It is classified into two types:
- Compile-time Polymorphism (Method Overloading)
- Runtime Polymorphism (Method Overriding)
1. Compile-time Polymorphism (Method Overloading)
Compile-time polymorphism is achieved by method overloading. Method overloading occurs when a class has multiple methods with the same name, but with different parameters (different number, type, or both).
Example 1: Method Overloading
In this example, we demonstrate compile-time polymorphism through method overloading:
class Calculator { // Method to add two integers int add(int a, int b) { return a + b; } // Overloaded method to add three integers int add(int a, int b, int c) { return a + b + c; } // Overloaded method to add two double values double add(double a, double b) { return a + b; } } public class Main { public static void main(String[] args) { Calculator calc = new Calculator(); System.out.println("Sum of 2 and 3: " + calc.add(2, 3)); // Calls add(int, int) System.out.println("Sum of 2, 3, and 4: " + calc.add(2, 3, 4)); // Calls add(int, int, int) System.out.println("Sum of 2.5 and 3.5: " + calc.add(2.5, 3.5)); // Calls add(double, double) } }
Output:
Sum of 2 and 3: 5 Sum of 2, 3, and 4: 9 Sum of 2.5 and 3.5: 6.0
In this example, the add()
method is overloaded in the Calculator
class to handle different types of parameters (integers and doubles) and different numbers of parameters. The method to be called is determined at compile time based on the method signature.
2. Runtime Polymorphism (Method Overriding)
Runtime polymorphism is achieved by method overriding. Method overriding occurs when a subclass provides a specific implementation of a method that is already defined in its superclass. The method that gets called is determined at runtime, based on the actual object type, not the reference type.
Example 2: Method Overriding
In this example, we demonstrate runtime polymorphism by overriding the sound()
method in subclasses of the Animal
class:
class Animal { // Base class method void sound() { System.out.println("Animals make sounds."); } } class Dog extends Animal { // Overridden method @Override void sound() { System.out.println("The dog barks."); } } class Cat extends Animal { // Overridden method @Override void sound() { System.out.println("The cat meows."); } } public class Main { public static void main(String[] args) { Animal myAnimal = new Animal(); // Reference of type Animal Animal myDog = new Dog(); // Reference of type Animal, but object of type Dog Animal myCat = new Cat(); // Reference of type Animal, but object of type Cat myAnimal.sound(); // Calls Animal's sound() method myDog.sound(); // Calls Dog's sound() method (runtime polymorphism) myCat.sound(); // Calls Cat's sound() method (runtime polymorphism) } }
Output:
Animals make sounds. The dog barks. The cat meows.
In this example, even though all references are of type Animal
, the actual method called depends on the object type (either Dog
or Cat
) at runtime. This is runtime polymorphism, where the method to be executed is determined at runtime.
3. Benefits of Polymorphism
Polymorphism offers several benefits, including:
- Code Reusability: Polymorphism allows you to write more flexible and reusable code by enabling you to use the same method or operator for different types of objects.
- Extensibility: Polymorphism allows subclasses to modify or extend the behavior of methods in the superclass, providing greater flexibility when adding new features to the program.
- Maintainability: By promoting the use of common interfaces and method signatures, polymorphism can make your code easier to maintain.
Example 3: Polymorphism with Interfaces
In this example, polymorphism is achieved by implementing an interface:
interface Animal { void sound(); // Abstract method } class Dog implements Animal { @Override public void sound() { System.out.println("The dog barks."); } } class Cat implements Animal { @Override public void sound() { System.out.println("The cat meows."); } } public class Main { public static void main(String[] args) { Animal myDog = new Dog(); // Reference of type Animal Animal myCat = new Cat(); // Reference of type Animal myDog.sound(); // Calls Dog's sound() method myCat.sound(); // Calls Cat's sound() method } }
Output:
The dog barks. The cat meows.
In this example, we use an interface Animal
with a method sound()
. Both the Dog
and Cat
classes implement this interface and provide their own implementation of the sound()
method. The actual method that gets called is determined at runtime based on the object type, demonstrating polymorphism through interfaces.
4. Conclusion
Polymorphism is a powerful feature in Java that allows methods to behave differently based on the objects they are acting on. Compile-time polymorphism (method overloading) occurs when the method signature is resolved during compilation, while runtime polymorphism (method overriding) happens when the method to be executed is determined at runtime, based on the object type. By using polymorphism, Java programs can be more flexible, reusable, and maintainable.