Object-oriented Programming: Polymorphism


Literally, "Poly" means many, "Morph" means form. So, Polymorphism = having many forms.
Polymorphism is a concept in Object-Oriented Programming (OOP) where an entity (variable, object, method) can change into other forms.
Real-world Example
Let's take the example of a person who can have many roles in their life.
At the Office, Person A behaves like an employee.
At the Mall, Person A behaves like a customer.
At Home, Person A behaves like a father.
Polymorphism can be achieved in two ways:
Compile-time Polymorphism (Method Overloading).
Run-time Polymorphism (Method Overriding).
Compile-time Polymorphism (Method Overloading)
Occurs when several methods have the same name but different parameters (in number, type, or order).
Method overloading can be done under several conditions:
Done on methods within the same class.
Done on methods within a child class.
Done on constructors.
Some rules for Method Overloading:
Parameters must differ (in number, type, or order).
Return type alone cannot distinguish overloaded methods.
Occurs at compile time (also called Compile-time Polymorphism).
Example code: Method overloading in the same class.
public class Person
{
public int SummingUp(int a, int b)
{
return a + b;
}
public int SummingUp(int a, int b, int c)
{
return a + b + c;
}
public double SummingUp(double a, double b)
{
return a + b;
}
}
In the code above, we can see that there are three methods with the same name in one class. Although they have the same name, these three methods have different parameters, so the compiler recognizes them as different methods, and no error will occur.
Example code: Inheritance-based method overloading.
class Person1
{
public void SummingUp(int a, int b)
{
Console.WriteLine(a + b);
}
public void SummingUp(float x, float y)
{
Console.WriteLine(x + y);
}
}
class Person2 : Person1
{
public void SummingUp(string s1, string s2)
{
Console.WriteLine(s1 +" "+ s2);
}
}
In the code above, we perform method overloading in different classes, but they are still derived from the previous class. This way, the compiler can also recognize the method as different, so this can be done without causing errors.
Example code: Constructor Overloading.
class Person
{
int x, y, z;
public Person(int x)
{
Console.WriteLine("Constructor1 Called");
this.x = 10;
}
public Person(int x, int y)
{
Console.WriteLine("Constructor2 Called");
this.x = x;
this.y = y;
}
public Person(int x, int y, int z)
{
Console.WriteLine("Constructor3 Called");
this.x = x;
this.y = y;
this.z = z;
}
public void Display()
{
Console.WriteLine($"X={x}, Y={y}, Z={z}");
}
}
Person obj1 = new Person(10);
obj1.Display();
Person obj2 = new Person(10, 20);
obj2.Display();
Person obj3 = new Person(10, 20, 30);
obj3.Display();
In the code above, we perform method overloading on the constructor along with an example of its implementation during initialization. This method is very advantageous because if at any time we want to add parameters to the constructor, we do not need to change the logic we have previously created when declaring objects. We just need to create a new constructor with different parameters and call it by adjusting the number and type of parameters.
Runtime Polymorphism (Method Overriding)
Method overriding occurs when a child class specifically implements a method that has been defined in its parent class. The method that allows for overriding needs to be added with a virtual modifier.
Done by adding the keyword 'override' to the method that performs the override.
The goal is for the child class to replace the behavior of the method inherited from its parent class.
Some rules for Method Overriding:
The modifier on the parent class method needs to be added with "virtual" and the modifier on the child class method needs to be added with "override".
The method in the child class must have the same name, return type, and parameters as its parent class.
Occurs at runtime (also called runtime polymorphism).
When do we need to override a method? If the logic in the parent class method cannot meet the business requirements of the child class.
Here is the example code.
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("The animal makes a sound.");
}
}
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("The dog barks.");
}
}
public class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("The cat meows.");
}
}
Animal myAnimal = new Animal();
myAnimal.Speak(); // "The animal makes a sound."
myAnimal = new Dog();
myAnimal.Speak(); // "The dog barks."
myAnimal = new Cat();
myAnimal.Speak(); // "The cat meows."
In the example code above, we perform method overriding on the Speak() method. In the parent class, we initially determined the implementation of the Speak() method, but in the child class, we override it by providing a new implementation for the Speak() method. So when the class is initialized into an object, we can call the method and produce different outputs according to the Object Type we use.
Note:
If there is no method override in the child class, the method from its parent class will be executed.
Example: If we delete the Speak() method in the Cat class, when executing the Speak() method for the Cat object type, the output will be: "The animal makes a sound," not "The cat meows" anymore.
What actually happens at compile time and run time?
At compile time, the compiler checks the reference type.
At run time, the CLR checks the object type.
This is why it is called compile-time polymorphism and runtime polymorphism.
The consequence is that when there is an implementation error related to the object type, the error will not be detected at compile time because the writing is considered correct. However, when run at runtime, the error will appear and be discovered.
We have learned about the concept of Polymorphism in OOP, next we will look at the last concept of OOP, which is Abstraction.
Subscribe to my newsletter
Read articles from Kristiadhy directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Kristiadhy
Kristiadhy
Experienced Full Stack .NET developer with a proven track record of designing and implementing robust business applications. Proficient in using ASP.NET Core Web API, Blazor, and WinForms to deliver high-quality, efficient code and scalable solutions. Strong focus on implementing industry best practices for cleaner and scalable outcomes.