Polymorphism can be visualized as a person who wears multiple disguises, at different times.
Technically, it means that a method / property behaves differently, at different times. These times could be captured at compile time / runtime
Polymorphism forms one of the most important pillars of OOPs paradigm. At an enterprise level, this principle is used on a day-in-and-out basis. There are 40 design patterns at the minimum that use the concept of polymorphism. The most common and obvious pattern is the "Factory" design pattern, which can be found even in the simplest and most complex enterprise projects.
In an interview, polymorphism questions come up in the first 5 questions.
There are two types of Polymorphism namely
- Compile-Time Polymorphism
- Overloading is the technique used to implement Compile-time polymorphism
- Run-Time Polymorphism
- Overriding is the technique used to implement Run-time polymorphism
- In overriding, the base class gives the permission to the derived classes to allow change in the implementation logic.
- This permission is given using special keywords
- virtual
- abstract
- interface
- Think of a person named "Kunal". Technically, "Kunal" is an object of the "Person" class, right?!
- A person can work. Hence, technically, "work" is a method inside the person class.
- Now a person can work on a single task, sometimes the person may work on multiple tasks. Technically, it means we can have two flavours of "work" viz.
- bool work(string task)
- bool work(string[] tasks)
- Both these methods are essentially having the same name and return type. The only change is the change in the parameter types and count. Also these methods are in the same class.
- Hence, in this case we can say that Work() is an overloaded method.
- Let's say the same Person "Kunal" is also an Employee.
- Technically, it means there is an IS-A relationship between Employee and Person, both of which are classes.
- For overriding, it is important that the classes involved are in an inheritance relationship.
- In this case, the Person object "Kunal", when started working in a company also became an object of "Employee" class, right?!
- The Person also works, and the Employee also works. This is a common behaviour between the two classes. But the way (implementation) work is done at office is different from the non-office environment. Both work on tasks, but the way they work and deliver is different.
- Technically, the logic is different for work() in Person, and different for works() in Employee
- work() is a method that returns bool. This declaration will remain the same whether the work is done at home or office, only the logic will differ.
- Hence, we are "overriding" only the implementation for work(), when we instantiate "Kunal" as person Vs when we instantiate "Kunal" as an Employee.
- In overriding, the method signature remains the same in both the base, derived classes. Only the logic is different.
- There are three ways of implementing overriding
- "virtual" keyword in base class --- "override" keyword in derived class
- "abstract" keyword in base class --- "override" keyword in derived class
- Interfaces also known as Code Contracts
1. Compile Time Polymorphism: Done by "overloading"
Same class, same function name & return type, multiple flavours of method by changing parameters
class Person{
public void Works(){}
public void Works(string pTask){}
}
2. Runtime Polymorphism: Done by "overriding"
Pre-requisite: Inheritance relation between classes
a. Using virtual
Mark a method as "virtual" in base class
Use "override" keyword to change the logic in derived class
** A method marked as virtual can have default implementation logic
Eg: class Shape{
public virtual PrintDetails(){
...default logic
}
}
class Circle : Shape{
public override PrintDetails(){
... different logic
}
}
b. Using abstract
Pre-requisite: Inheritance relation between classes
** Abstract means Incomplete
** A method / property can be marked as abstract
** If a method / property is marked as abstract, the class MUST be marked as abstract
** An abstract class can NEVER be instantiated
** Hence, use base = new derived() formula
i. Mark a method / property as abstract in base class.
ii. Mark the base class as abstract
iii. In derived class use "override" keyword
Eg: abstract class Shape{
public abstract void Draw();
}
class Circle : Shape{
public override void Draw(){
...logic
}
}