Polymorphism (from the Greek words poly meaning "many" and morph meaning "forms") is the ability of a single entity (method, operator, or object) to take on multiple forms. It is the fourth pillar of OOP.
There are two fundamental types of polymorphism:
Method Overloading allows a class to have multiple methods with the same name but with different parameter lists (different number of parameters, different types of parameters, or both).
The compiler determines which version of the overloaded method to call based on the arguments provided at the call site. This decision is resolved entirely at compile time.
class Calculator {
// Version 1: Two integers
int add(int a, int b) {
return a + b;
}
// Version 2: Three integers
int add(int a, int b, int c) {
return a + b + c;
}
// Version 3: Two doubles
double add(double a, double b) {
return a + b;
}
}
Calculator calc = new Calculator();
calc.add(2, 3); // Calls Version 1 -> returns 5
calc.add(2, 3, 4); // Calls Version 2 -> returns 9
calc.add(2.5, 3.5); // Calls Version 3 -> returns 6.0
int add(int a) and double add(int a) will cause a compile error because the compiler cannot distinguish which one to call from calc.add(5).As we saw in the Constructors chapter, constructors can also be overloaded. This is a very common use case of compile-time polymorphism.
class Student {
Student() { /* default */ }
Student(String name) { /* with name */ }
Student(String name, int age) { /* with name and age */ }
}
Operator Overloading allows you to redefine the behavior of standard operators (+, -, *, ==) for user-defined classes.
+ operator which is pre-overloaded for String concatenation).// C++ Operator Overloading Example
class Complex {
double real, imag;
public:
Complex operator+(const Complex& other) {
Complex result;
result.real = this->real + other.real;
result.imag = this->imag + other.imag;
return result;
}
};
Complex a, b, c;
c = a + b; // The '+' operator now works on Complex objects!
The binding between the method call and the method body happens at compile time because the compiler has all the information it needs: the method name and the argument types. It doesn't need to wait until the program is running to figure out which add() version to use. This makes overloaded method calls extremely fast, with zero runtime overhead.