Unlock OOP: Polymorphism Techniques You Need to Master!

Polymorphism describes the concept that objects of different types can be accessed through the same interface. Each type can provide its own, independent implementation of this interface.

So; is polymorphism just “being able to perform functions of an interface, such as adding, subtracting, etc, on objects of different data types such as integers, floats, etc”?
Is it basically operator overloading?
Or templating?

First let us understand What is Overloading and Overriding.

Overloading and Overriding

-> When two or more methods in the same class have the same name but different parameters, it’s called Overloading.
-> When the method signature (name and parameters) are the same in the super-class and the child class, it’s called Overriding.

# Overriding implements Runtime Polymorphism whereas Overloading implements Compile time polymorphism.
# The method Overriding occurs between super-class and subclass. Overloading occurs between the methods in the same class.
# Overriding methods have the same signature i.e. same name and method arguments. Overloaded method names are the same but the parameters are different.
# With Overloading, the method to call is determined at the compile-time. With overriding, the method call is determined at the runtime based on the object type.

Let’s Look at Polymorphism

Polymorphism is just a fancy word which means we can use a more general term to refer to a specific type of object. It goes hand in hand with interfaces.
Interface: Same word, several flavours.
Instead of saying “I got a new Ferrari”, we could simply say “I got a new car”. This statement would also be true if we’d just got a Ford Fiesta, as that is also a car. The flexibility (polymorphism) of the English word ‘car’ means that we don’t have to specify exactly which kind of car it is. Our audience will know that we have a modern contraption on our garage which is designed to beep, steer, and drive down the road, even though the exact mechanisms of Ferrari and Ford engines may be different from each other.

Polymorphism takes this interface and lets us refer to your Ford Fiesta as simply a Car:
Car car = new Ford();

Polymorphism means using a super-class variable to refer to a subclass object.
For example, consider this simple inheritance hierarchy and code:

abstract class Animal {
    abstract void talk();
class Dog extends Animal {
    void talk() {
class Cat extends Animal {
    void talk() {

Polymorphism allows us to hold a reference to a Dog object in a variable of type Animal, as in:
Animal animal = new Dog();

Given a base class shape, polymorphism enables the programmer to define different area methods for any number of derived classes, such as circles, rectangles and triangles. No matter what shape an object is, applying the area method to it will return the correct results.
If I have a Shape that has attribute type = triangle, and another Shape that has attribute type = square, and I call the .area() method, the function returns different equations depending on the type. In essence this is polymorphism.
If we process a collection of Shape that are a mixture of different types of Shape, calling area() on each would be done polymorphically.

Polymorphism vs Inheritance

polymorphism can rely on inheritance, which makes it a little confusing.
The idea behind polymorphism is that the base class defines the methods, and the sub-classes define the implementation. In other words, the base class masks the subclass, but the subclass does the work.
The idea behind inheritance is that the subclass inherits the implementation from the base class, so it doesn’t need to implement it. The subclass masks the base class, but the base class does the work.

in many languages, integers and floats are implicitly polymorphic since we can add, subtract, multiply and so on, irrespective of the fact that the types are different. In that same way, a class like BigDecimal or Rational or Imaginary can also provide those operations, even though they operate on different data types.

The classic example is the Shape class and all the classes that can inherit from it – square, circle, irregular polygon and so on. With polymorphism, each of these classes will have different underlying data. A point shape needs only two co-ordinates. A circle needs a centre and a radius. A square or rectangle needs two co-ordinates for the top left and bottom right corners and possibly a rotation. An irregular polygon needs a series of lines.

By making the class responsible for its code as well as its data, we can achieve polymorphism. In this example, every class would have its own Draw() function and the client code could simply do:
shape.Draw() to get the correct behaviour for any shape.

Polymorphism describes a pattern in object oriented programming in which classes have different functionality while sharing a common interface.

The beauty of polymorphism is that the code working with the different classes does not need to know which class it is using since they’re all used the same way. A real world analogy for polymorphism is a button. Everyone knows how to use a button: we simply apply pressure to it. What a button “does,” however, depends on what it is connected to and the context in which it is used — but the result does not affect how it is used.

Duck typing

This term comes from the saying – “If it walks like a duck, and it quacks like a duck, then it must be a duck”. Duck typing is a concept related to dynamic typing, where the type or the class of an object is less important than the methods it defines. When we use duck typing, we do not check types at all. Instead, we check for the presence of a given method or attribute. The idea is that we don’t need a type in order to invoke an existing method on an object – if a method is defined on it, we can invoke it.

Example task: Call some method Quack on an object.

Without using duck-typing, a function f doing this task has to specify in advance that its argument has to support some method Quack. A common way is the use of interfaces

interface IQuack { 
    void Quack();

void f(IQuack x) { 

Calling f(42) fails, but f(donald) works as long as donald is an instance of a IQuack-subtype.
So how does duck typing change this?
Well, a duck typing system does not specify requirements but just tries if anything works.

Thus, a dynamic type system as Python’s always uses duck typing:

def f(x):

If f gets an x supporting a Quack(), everything is fine, if not, it will crash at runtime.

One of the problems is that most OO languages permit things to be built in a way that they should not be built. That isn’t special to OO, of course. You can write 20 page functions with if and while statements nested 15 deep in C, of course, though no one can understand or maintain the code. Likewise you can use concrete sub-classing in a way that builds equally badly formed software. One of the first examples of sub-classing to appear in many tutorial was that a Circle class was a subclass of a Point class. It was done this way because it was easy to do, not because it was sensible. A circle isn’t a special kind of point. But to build the Circle class from a Point class we could just add a single field for the radius and use the point coordinates for the centre.

But it makes no sense.

The Animal hierarchy is unsuitable for teaching about polymorphism unless we remember that the entire hierarchy consists of abstractions, not concrete “objects”. There are no instantiations of class Mammal, or Vertebrate. Those are merely abstractions.

Likewise Vehicle is a poor vehicle for teaching polymorphism as there are too many variations in the concrete things that carry that description. A bicycle, a golf cart, an automobile, and a tractor-trailer rig are all Vehicles. So is a Tank. They have very little in common other than mobility and the capability of carrying things. If we try to build a “hierarchy” of such things, the variations will overwhelm the commonalities.
They are more “unlike” than they are “like”, in fact.
Just as for Animal.

Why it Doesn’t Work

In building good software we need to manage complexity. Complexity is what kills us. It makes our programs buggy if not well managed and it also makes them hard to maintain. Suppose that we are at a point in a program in which we, the programmer, have built a structure that required ten thousand decisions to be made and the software didn’t hide any of them from us at the current line we are writing. We need to take account of every decision that has been made. It is impossible. Software is intended, actually, to hide such decisions. Polymorphism is a way to manage complexity if used well, but since it can be used badly it can also add to the complexity burden.
So, we need a way to use it well.

For example, suppose that we have a List of Vehicles. Suppose that Vehicle is a super-class and that every subclass of Vehicle has added different public features. The bicycle and tractor-trailer classes know about number of gears, but it is irrelevant for golf carts. All is well until we try to remove items from the List and operate on them. All the program knows about them is that they are vehicles unless we provide a way for the program to recapture the most specific class. But having to ask instanceof questions or do some sort of class based switch means that we have completely given up the advantages of polymorphism. We need to recapture the most specific thing when polymorphism is supposed to let us deal with general things. We are back to writing if statements when they shouldn’t be needed.

Polymorphism: How to Make it Work

Suppose that instead of thinking of using polymorphism at a large scale, think of it at a much smaller scale. Don’t try to get some polymorphic sense about vehicle overall, but think about how vehicles are actually built in the real world.
I’ll take bicycle as a simple example.

The bike I’m thinking of is composed of parts. It has a seat, wheels, gears, etc. I’ll focus on just those. It is is suitable for trail riding and for expeditions and even for the road. When we first purchase; it the bike is set up primarily for moderately comfortable use on mild trails. It work well for that.
But the parts are all replaceable. By changing the saddle we can make it better fit my anatomy. By changing the wheels I can make it much nicer on the road – both lighter and with a smoother ride. And by changing the gears – even the number of gears we can make it more useful for serious rocky trails or for more modest prepared surfaces.

This is polymorphism used well.

We can easily swap parts and by giving it one part at one time and a different part at another time we can give it different behaviour. It is the behaviour that is polymorphic, not the interface. The two sets of wheels we have for this bike are interchangeable, but behave very differently. The saddles enable one use or another, for one rider or another. But the interface between the saddle and the rest of the bike is fixed. And we don’t need to remember each time we switch gears whether we have the road wheels or the trail wheels on the bike. It “works” the same, but “behaves” differently without any decisions (if-this-then-that) on our part as we ride.

We can build software this way also. Build complex objects with composition – lots of part. Put the polymorphism in the parts. Each kind of part has an interface that doesn’t need to be tailored to use (i.e. no added public features in sub-classes). When the object of which these polymorphic things are a part needs to change its behaviour, swap out one part for another.
The overall object’s interface didn’t change, but its behaviour does.

This is how automobiles are built these days. We can buy most cars with different engines, different seats, different entertainment systems, etc. But each of those various engines presents the same interface to the rest of the automobile. Therefore they are swap-able without modifying the chassis, and by swapping them they change the behaviour of the object of which they are a part.

Leave a Comment