What is Polymorphism?
Polymorphism is one of the Most important features of Object-Oriented Programming. This allows to Single task or action can be performed in different ways. The word “poly” means many and “morphs” means forms, So it means many forms.
This concept allows objects of different types to be treated as objects of a common type.
There are two main types of polymorphism in Kotlin:
- Compile-time polymorphism (also known as static polymorphism)
- Runtime polymorphism (also known as dynamic polymorphism).
Compile-Time Polymorphism(Method Overloading):
It is also known as static polymorphism. Method overloading allows a class to have multiple methods with the same name but different parameter lists.
The compiler determines which method to call based on the number or types of parameters at compile-time.
class MathOperations {
fun add(x: Int, y: Int): Int {
return x + y
}
fun add(x: Double, y: Double): Double {
return x + y
}
}
fun main() {
val math = MathOperations()
println(math.add(1, 2)) //3 // Calls the first method
println(math.add(2.3, 2.3)) //4.6 // Calls the second method}
}
In this example, the add method is overloaded with different parameter types, allowing the same method name to be used for both integer and double addition.
The compiler decides at compile-time which version of the method to invoke based on the number and data types of arguments. This allows the same method name to be used for different types or numbers of parameters, providing flexibility and readability in the code.
Runtime Polymorphism (Method Overriding):
It is also known as dynamic polymorphism or Dynamic Method Dispatch.
Method overriding occurs when a subclass provides a specific implementation for a method that is already defined in its superclass. The overridden method in the subclass has the same signature (name and parameters) as the method in the superclass.
The decision on which method to call is made at runtime based on the actual type of the object. Runtime polymorphism is achieved through inheritance and method overriding. let’s take an example
// Base class representing a generic vehicle
open class Vehicle {
open fun accelerate() {
println("Generic vehicle accelerating")
}
}
// Subclass representing a Car
class Car : Vehicle() {
override fun accelerate() {
println("Car accelerating: Vroom Vroom!")
}
}
// Subclass representing a Motorcycle
class Motorcycle : Vehicle() {
override fun accelerate() {
println("Motorcycle accelerating: Vroom Vroom!")
}
}
fun main() {
val vehicles: Array<Vehicle> = arrayOf(Car(), Motorcycle()))
for (vehicle in vehicles) {
vehicle.accelerate() // Calls the overridden accelerate method in each subclass
}
}
Explaination:
In this Example, for the Car and Motorcycle class we have provided specific implementation for accelerate(), The overridden method in each subclass is invoked based on the actual type of the object.
During each iteration, the actual type of the object in the array determines which version of the accelerate method is called.
If the current object is an instance of Car, the overridden accelerate method in the Car class is executed. Similarly, if it’s an instance of Motorcycle, the accelerate method in the Motorcycle class is executed.
The output of the program will be:
Car accelerating: Vroom Vroom!
Motorcycle accelerating: Vroom Vroom!
Advantage of Polymorphism
- Code Reusability: Polymorphism provides the reuse of code, allowing a method with same name can be used by multiple classes.
- Flexibility: With polymorphism, you can write code that can work with objects of different types at runtime, without having to know the exact type of object you’re working with ahead of time.
- Reduced Complexity: Polymorphism simplifies code by allowing the use of the same method or interface name for related functionalities. It Enhances code readability and makes maintenance easier by reducing complexity.
- Improved Maintainability: Changes can be made more efficiently without causing a ripple effect throughout the entire codebase, leading to improved maintainability.
Disadvantages of Polymorphism
- Debugging Challenges: Polymorphic behavior can sometimes make it challenging to trace the flow of execution during debugging. Identifying which method is actually called at runtime can be more complex, making debugging more challenging.
- Runtime Errors: Errors related to polymorphism might only show up when the program is running. This can lead to unexpected issues that could have been caught earlier.
- Potential Misuse: Incorrect use of polymorphism can result in hard-to-maintain code. The system may become difficult to extend or modify.