Polymorphism
A core concept in object-oriented programming that allows objects of different classes to be treated as objects of a common superclass. It enables a single interface to represent different underlying forms (data types).
1960s
2
Definitions
Polymorphism in Object-Oriented Programming
In object-oriented programming (OOP), polymorphism is a principle that allows objects of different classes to be treated as objects of a common superclass. It is the ability of a single variable, function, or object to take on many forms. This is typically achieved through inheritance and interfaces, where a base class or interface defines a common contract (e.g., a method signature), and multiple derived classes provide their own specific implementations of that contract.
Key Concepts
- Inheritance: A mechanism where a new class (subclass or derived class) inherits properties and behaviors (methods) from an existing class (superclass or base class).
- Method Overriding: A feature where a subclass provides a specific implementation for a method that is already defined in its superclass. This is the primary mechanism for runtime polymorphism.
- Interface: A contract that defines a set of method signatures. A class that implements an interface must provide an implementation for all its methods.
Example: The Shape Analogy
Imagine a base class Shape with a method draw(). Several other classes like Circle, Square, and Triangle can inherit from Shape and provide their own unique implementation of the draw() method.
// Pseudocode/Java Example
abstract class Shape {
abstract void draw();
}
class Circle extends Shape {
void draw() {
System.out.println("Drawing a circle.");
}
}
class Square extends Shape {
void draw() {
System.out.println("Drawing a square.");
}
}
// Polymorphic usage
Shape myShape1 = new Circle();
Shape myShape2 = new Square();
myShape1.draw(); // Outputs: Drawing a circle.
myShape2.draw(); // Outputs: Drawing a square.
In this example, a variable of type Shape can hold an object of type Circle or Square. When the draw() method is called, the Java Virtual Machine (JVM) determines at runtime which specific draw() method to execute based on the actual object's type. This is also known as late binding or dynamic binding.
Compile-time vs. Runtime Polymorphism
Polymorphism can be broadly classified into two types based on when the method call is resolved: at compile time or at runtime.
Compile-time Polymorphism (Static Polymorphism)
Also known as static binding or early binding. The decision of which function to execute is made by the compiler at compile time. This is achieved through:
- Method Overloading (or Function Overloading): Defining multiple methods within the same class that have the same name but different parameters (either in number, type, or order). The compiler selects the correct method based on the arguments passed during the method call.
class Calculator { int add(int a, int b) { return a + b; } double add(double a, double b) { return a + b; } } // Compiler knows which 'add' to call based on argument types. - Operator Overloading: Allowing operators (like
+,-,*) to have different meanings for different data types. This is common in languages like C++ and Python, but not supported in Java.
Runtime Polymorphism (Dynamic Polymorphism)
Also known as dynamic binding or late binding. The method to be executed is determined at runtime, not at compile time. This is the most common form of polymorphism in OOP and is achieved through:
- Method Overriding: A subclass redefines a method from its superclass. When a method is called on an object through a superclass reference, the version of the method in the actual object's class (the subclass) is executed.
// Using the Shape example from the previous definition Shape shape = new Circle(); // Superclass reference, subclass object shape.draw(); // The 'draw' method from the Circle class is called at runtime.
This form of polymorphism is fundamental for creating flexible and extensible software systems, as new derived classes can be added without modifying the code that uses the base class.
Origin & History
Etymology
The term 'polymorphism' originates from the Greek words 'polys' (πολύς), meaning 'many' or 'much,' and 'morphē' (μορφή), meaning 'form' or 'shape.' Combined, it literally translates to 'many forms.'
Historical Context
The conceptual roots of polymorphism are tied to the birth of object-oriented programming. * **1960s**: The Norwegian Computing Center developed the **Simula** language, considered the first object-oriented programming language. Simula introduced classes, objects, inheritance, and, crucially, **virtual methods**, which are the mechanism for implementing runtime polymorphism. This allowed a procedure call to execute different code depending on the class of the object it was called on. * **1970s**: At Xerox PARC, Alan Kay and his team developed **Smalltalk**. Smalltalk fully embraced the object-oriented paradigm, making polymorphism a central and natural feature. Its 'message passing' model, where objects respond to messages, is inherently polymorphic—the action taken depends entirely on the receiving object's type. * **1980s**: Bjarne Stroustrup developed **C++**, which brought object-oriented features, including polymorphism, to the widely-used C language. C++ formalized the concept of `virtual` functions, requiring developers to explicitly mark methods that could be overridden to enable runtime polymorphism (**dynamic binding**). * **1990s**: The release of languages like **Java** and **Python** cemented polymorphism as a mainstream programming concept. Java, for instance, made methods virtual by default (unless marked as `final` or `static`), simplifying the use of polymorphic behavior compared to C++. This made it easier for developers to write flexible and extensible code.
Usage Examples
In our graphics application, we use polymorphism to treat all Shape objects—whether a Circle, Square, or Triangle—uniformly in the rendering loop.
The framework's plugin system relies heavily on polymorphism; you can create a new plugin by simply implementing the IPlugin interface, and the core system will call its methods without knowing its specific type.
By using a base class pointer to refer to a derived class object, C++ achieves runtime polymorphism, also known as dynamic binding, through virtual functions.
Frequently Asked Questions
What is the difference between method overloading and method overriding?
Method overloading is a form of compile-time polymorphism where multiple methods have the same name but different parameters (type, number, or order). Method overriding is a form of runtime polymorphism where a subclass provides a specific implementation for a method that is already defined in its superclass.
How does polymorphism promote code reusability and flexibility?
It allows you to write generic code that can work with objects of different classes through a common interface. This means you don't have to write separate code to handle each specific subclass. New classes can be added that adhere to the common interface without changing the existing generic code, making the system flexible and extensible.
What is another term for runtime polymorphism?
Dynamic binding or late binding.