While studying object oriented programming, we learnt that ‘Polymorphism’ is one the fundamental feature of the object oriented programming. The simple definition of polymorphism means occurrence of something in different forms. Is it so? Then, water can exist as ice and vapors, so can we call it as polymorphism? Technically, yes. Now, in the programming world, why we need something to exists in different forms? Can we live without them?
In object oriented programming, the way to perceive the code blocks is different from when we talk about our favorite procedural programming. In OOP, the code block has to be class, which when instantiated, becomes an object, and it should have defined features, capable of performing defined actions, and defined sets of reactions too. This object should be more like a living thing.
The purpose of polymorphism is to implement a different style of programming in which objects of different types define a common interface of operation for users. So, if we talk about an odd example – your hands. There is a common interface of your hand, and it can be used to hold things, move things, etc. This type of style is called ‘message-passing’.
When programming in OOP, our objectivity is create the code blocks called ‘classes’, and ensure that these classes provides common interfaces to it’s users (which can be any other class – which will be written by the same programmer or some other programmer), so that they have easy life. This can be explained by:
The printf function can takes multiple parameters, any data types: this means that instead of having my printf functions (e.g. printf1, printf2, or printfstring, printfsint, etc), we just need to know that printf is the function using which we can print data (irrespective of the type). Don’t you think that this has eased the life of ours a bit?
So, while building our own code blocks (classes), we may have to look at this aspect, and see where we need to use the ‘message-passing’ style of coding. I will use the classic example here – write a code block that is capable of calculating the area of different shapes. Here the operation is to calculate the area, while shapes can be rectangle, circle or square.
First, we will touch on something called – Overloading. This is quite simple, very much like the ‘printf’ example quoted above. In function overloading, two or more functions will have same name, but operate on different types of data. If we consider the operation of calculating the area of shapes – for rectangle, we need length and breadth, for circle, we need only radius, and for square, we need only length. For the example sake, we are going to use different data types – for radius, we will accept a float as datatype, while for square, we will accept integer as datatype. Thus, we can overload the functions by:
Having different number of arguments
Having different datatype for arguments.
Let’s look at the code now (implemented in C#).
//Define single class for calculating areas of different shapes
class Area
{
//Define variables
float area;
//Declare different forms of Functions –
public void cal_area(int length ,int breadth)
{
area=length*breadth;
Console.WriteLine(“Area of Rectangle:”,area);
}
public void cal_area(int length)
{
area=length*length;
Console.WriteLine(“Area of Square:”,area);
}
public void cal_area(float radius)
{
area=3.14*radius*radius;
Console.WriteLine(“Area of Circle:”,area);
}
}
Let’s now use this class (implement) – code given below. Now how the compiler would know which function to call? Given the parameters of a particular shape, the choice is made on the basis of number or type of arguments.
Area a;
a.cal_area(5); //calls the cal_area(int) function
a.cal_area(5,6); //calls the cal_area(int, int) function
a.cal_area(3.5); //calls the cal_area(float) function
In the above example, the decision of which function to be executed is made by the compiler at compile time, this type of overloading is also called ‘compile-type’ polymorphism.
Now, let’s touch on another aspect called – Overriding. This is related to another fundamental feature of OOP – inheritance. So, what if an inherited class wants to do differently from it’s parent class for function? Can inherited class do that? Yes, in this case the inherited class (of the sub class) has to override the function.
Let’s look at the same example of calculating area in ‘overriding’ way (implemented in C++).
//parent class with variable to store area, and a virtual function to calculate area
class Shape
{
public double area;
public virtual void cal_area(){}
}
//Inherited from shape, with it’s own implementation of area.
class Circle:Shape
{
public float radius;
public void cal_area()
{
area=3.14*radius*radius;
}
}
//Inherited from shape, with it’s own implementation of area.
class Square: Shape
{
public int side;
public void cal_area()
{
area=side*side;
}
}
//Inherited from shape, with it’s own implementation of area.
class Rectangle: Shape
{
public int length;
public int breadth;
public void cal_area()
{
area=length*breadth;
}
}
In the above code, we have 4 classes, 1 parent, and 3 sub classes. All the sub classes, has it’s own way of calculating area (different cal_area function). Here, we have overridden the cal_area in all the sub classes. So, this is called function overriding. Now, let’s have a look at it’s implementation.
Shape *s1;
Circle c;
c.radius = 10.5;
Square s;
s.side = 5;
Rectangle r;
r.length = 8;
r.breadth = 4;
s1=&c;
s->cal_area();
s1=&s;
s->cal_area();
s1=&r;
s->cal_area();
In this example, the base class pointer contains address of derived class, the decision of calling different versions of virtual function is taken at the time of execution of code – runtime, hence the term runtime polymorphism.
The primary usage of polymorphism in industry (object-oriented programming theory) is the ability of objects belonging to different types to respond to method, field, or property calls of the same name, each one according to an appropriate type-specific behavior. The programmer (and the program) does not have to know the exact type of the object in advance, and so the exact behavior can be determined at compilation time or at runtime (this is also called late binding or dynamic binding).