Polymorphism
Polymorphism#
Polymorphism allows objects of different types to be treated through a common interface while maintaining their specific behavior. It enables a single method call to behave differently depending on the actual object type at runtime through dynamic method dispatch.
Core Concept#
Polymorphism promotes code flexibility and extensibility by allowing new types to be added without modifying existing code. Instead of writing separate methods for each type, you can write a single method that works with any object implementing the common interface.
Example: Instead of playGuitar(), playPiano(), and playViolin(), you write one playInstrument() method that works with any musical instrument.
How Polymorphism Works#
graph LR
A[Client Code] --> B[playInstrument method]
B --> C[Instrument Interface]
style A fill:#e8f5e8
style B fill:#e3f2fd
style C fill:#c8e6c9
Runtime Decision Making: The program determines which specific implementation to execute based on the actual object type.
graph TD
C[Instrument Interface] --> D[Guitar.play]
C --> E[Piano.play]
C --> F[Violin.play]
C --> G[Drums.play]
D --> H["Strumming strings"]
E --> I["Pressing keys"]
F --> J["Bowing strings"]
G --> K["Hitting percussion"]
style C fill:#c8e6c9
style D fill:#ffecb3
style E fill:#ffecb3
style F fill:#ffecb3
style G fill:#ffecb3
Benefits#
- Extensibility: Add new types without modifying existing code
- Maintainability: Single interface for multiple implementations
- Scalability: Build frameworks that work with future classes
Implementation Examples#
public abstract class Instrument {
public abstract void play();
}
public class Guitar extends Instrument {
@Override
public void play() {
System.out.println("Strumming the guitar!");
}
}
public class Piano extends Instrument {
@Override
public void play() {
System.out.println("Playing the piano!");
}
}
public void playInstrument(Instrument instrument) {
instrument.play(); // Dynamic method invocation
}
abstract class Instrument {
abstract fun play()
}
class Guitar : Instrument() {
override fun play() {
println("Strumming the guitar!")
}
}
class Piano : Instrument() {
override fun play() {
println("Playing the piano!")
}
}
fun playInstrument(instrument: Instrument) {
instrument.play() // Dynamic method invocation
}
abstract class Instrument {
abstract play(): void;
}
class Guitar extends Instrument {
play(): void {
console.log("Strumming the guitar!");
}
}
class Piano extends Instrument {
play(): void {
console.log("Playing the piano!");
}
}
function playInstrument(instrument: Instrument) {
instrument.play(); // Dynamic method invocation
}
abstract class Instrument {
void play();
}
class Guitar extends Instrument {
@override
void play() {
print("Strumming the guitar!");
}
}
class Piano extends Instrument {
@override
void play() {
print("Playing the piano!");
}
}
void playInstrument(Instrument instrument) {
instrument.play(); // Dynamic method invocation
}
class Instrument {
func play() {
fatalError("This method must be overridden")
}
}
class Guitar: Instrument {
override func play() {
print("Strumming the guitar!")
}
}
class Piano: Instrument {
override func play() {
print("Playing the piano!")
}
}
func playInstrument(instrument: Instrument) {
instrument.play() // Dynamic method invocation
}
from abc import ABC, abstractmethod
class Instrument(ABC):
@abstractmethod
def play(self):
pass
class Guitar(Instrument):
def play(self):
print("Strumming the guitar!")
class Piano(Instrument):
def play(self):
print("Playing the piano!")
def play_instrument(instrument: Instrument):
instrument.play() # Dynamic method invocation