Java Polymorphism
Polymorphism is one of the key principles of Object-Oriented Programming (OOP) in Java that allows objects to be treated as instances of their parent class, enabling a single interface to represent different underlying forms (data types). The term "polymorphism" means "many shapes" and refers to the ability of a method or an object to take many forms.
Types of Polymorphism in Java
Compile-time Polymorphism (Static Polymorphism):
- Achieved through method overloading and operator overloading.
- Involves defining multiple methods with the same name but different parameter lists (type, number, or both).
Runtime Polymorphism (Dynamic Polymorphism):
- Achieved through method overriding, where a subclass provides a specific implementation of a method that is already defined in its superclass.
- The decision about which method to invoke is made at runtime based on the object type.
1. Compile-time Polymorphism
Method Overloading
In method overloading, multiple methods can have the same name but must differ in the type or number of parameters. The correct method to be called is determined at compile time.
Example of Method Overloading:
class MathOperations {
// 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;
}
}
// Main class to demonstrate method overloading
public class Main {
public static void main(String[] args) {
MathOperations math = new MathOperations();
System.out.println(math.add(5, 10)); // Output: 15
System.out.println(math.add(5, 10, 15)); // Output: 30
System.out.println(math.add(5.5, 10.5)); // Output: 16.0
}
}
Explanation:
- The
MathOperations
class contains three overloadedadd
methods. The appropriate method is selected at compile time based on the arguments passed.
2. Runtime Polymorphism
Method Overriding
In method overriding, a subclass provides a specific implementation of a method that is already defined in its superclass. The method invoked is determined at runtime based on the object type, allowing for dynamic behavior.
Example of Method Overriding:
// Superclass
class Animal {
void sound() {
System.out.println("Animal makes a sound.");
}
}
// Subclass
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks.");
}
}
// Another subclass
class Cat extends Animal {
@Override
void sound() {
System.out.println("Cat meows.");
}
}
// Main class to demonstrate runtime polymorphism
public class Main {
public static void main(String[] args) {
Animal myAnimal; // Declare a reference variable of type Animal
// Create a Dog object and assign it to the Animal reference
myAnimal = new Dog();
myAnimal.sound(); // Output: Dog barks.
// Create a Cat object and assign it to the Animal reference
myAnimal = new Cat();
myAnimal.sound(); // Output: Cat meows.
}
}
Explanation:
- In this example, the
Animal
class has a methodsound()
, which is overridden in both theDog
andCat
subclasses. - The variable
myAnimal
is of typeAnimal
, but it can reference any object that is a subclass ofAnimal
. - When
myAnimal.sound()
is called, the JVM determines which method to execute at runtime based on the actual object type, not the reference type.
Advantages of Polymorphism:
- Code Reusability: Allows methods to be reused across different classes with similar behaviors, reducing code duplication.
- Flexibility and Extensibility: New subclasses can be added with specific behaviors without modifying existing code, making it easier to extend and maintain.
- Dynamic Method Binding: Supports runtime polymorphism, where the method to be executed is determined at runtime, allowing for dynamic and flexible code execution.
Summary:
- Polymorphism allows objects to be treated as instances of their parent class, enabling multiple forms and enhancing code flexibility.
- Compile-time polymorphism is achieved through method overloading, while runtime polymorphism is achieved through method overriding.
- Polymorphism is a powerful feature in Java that promotes code reusability, flexibility, and maintainability, allowing developers to create more dynamic and modular applications.