Polymorphism in Object Oriented Programming
Polymorphism means “many forms. By its name, it is a feature that allows you to perform an action in multiple or different ways. The most common form of Polymorphism is where you have a hierarchy of classes and you can substitute child classes for parent classes. That is why Polymorphism is also known as Subtyping.
Note: Examples of this and its related articles are related to high level statically typed object oriented languages such as C# and Java.
Dual nature of Polymorphism
In Object Oriented Programming, Polymorphism is the ability of an object to take on many forms.
1. One object can be taken as different forms — a Dog instance can be taken both as Dog and as Animal.
2. A variable can refer to instances of different types as long as they conform — an Animal variable can refer to a Dog or Cat instance.
Conceptual Aside — why are we referring to inheritance for polymorphism?
Inheritance enables most widely used form of polymorphism in C#, Java and other high level OOP languages.
Even when inheritance plays an important role in the implementation of some of these forms of polymorphism, certainly it is not the only way. Other languages that are not object-oriented provide other forms of polymorphism. Take, for example, the cases of duck typing in dynamic languages like Python or even in statically-typed languages like Go, or algebraic data types in languages like SML, Ocaml and Scala, or type classes in languages like Haskell, multi methods in Clojure, prototypal inheritance in JavaScript, etc.
Types of polymorphism
The best explanation on the subject that I’ve ever read is an article by Luca Cardelli, a renown type theorist. The article is named On Understanding Types, Data Abstraction, and Polymorphism.
Cardelli defines several types of polymorphism in this article:
- Universal
- Parametric (Early binding)
- Inclusion (Subtype) - Ad-hoc
- Overloading
- Coercion (Casting)
The kind of polymorphism related to inheritance is classified as inclusion polymorphism or subtype polymorphism.
Conceptual Aside — Ad-hoc vs Coercion
Many people consider Overloading as the only Ad-hoc polymorphism and that Coercion Polymorphism is a different kind rather than a sub-type of Ad-hoc polymorphism.
Parametric polymorphism — early binding — compile time
Parametric Polymorphism opens a way to use the same piece of code for different types. It is implemented by the use of generics.
Example — parametric polymorphism
Inclusion polymorphism — subtyping — dynamic — late binding — runtime
The Inclusion polymorphism is called as subtyping. This allows us to point to derived classes using base class references. This is a runtime polymorphism. We do not know type of the actual object until it is executing.
This type of polymorphism can be achieved by using inheritance and method overriding.
Short Example — using inheritance and method overriding
Here is an example. Suppose you have various cats like these felids.
Since they are all of Felidae biological family, and they all should be able to meow, they can be represented as classes inheriting from Felid base class and overriding the meow pure virtual function.
When to use method overriding — with a big example
Suppose we have a class Canvas whose purpose is to draw shapes and for that we need to pass it shapes such as Triangle, Circle or Rectangle. These Shapes are distinguished by a type.
Problems in this approach of drawing shapes are:
- Canvas knows internals of all shapes. Canvas class is aware of type that distinguishes each shape.
- It violates Abstraction which means classes should not know irrelevant internal behavior of other classes.
- Canvas class contains the algorithms for drawing all shapes. Algorithm for drawing a shape should be contained in a class related to that shape. Thus it’s doing something it’s not supposed to. It violates Single Responsibility Principle which states that each class is supposed to be responsible for only one purpose.
- If we have to add new shape for e.g. Triangle then Canvas class will have to be changed too. Thus it violates Open Closed Principle which states that class or functionality should be closed for modification but open for extensive behavior.
Let’s fix this code by getting rid of ShapeType and instead having classes for each type i.e. Circle and Rectangle that inherit from Shape. This Shape class will also contain Draw method which can be overridden by Circle and Rectangle classes.
Now if we add Triangle shape to this application then all we have to do is create a class Triangle inheriting from Shape and override Draw method and then create it’s object which we can pass to Canvas.DrawShapes() method. Our Canvas class will not be modified for as many shapes as we would like to add. Further improvements can be done such as if we can abstract away the Draw method from Shape class to IDraw interface.
Ad-hoc polymorphism — static — early binding — compile time
This allows function with same name to act in different manner for different types. The function and the operator both can be overloaded. Some language does not support operator overloading, but function overloading is common. For example: the + operator adds two integers and concatenates two strings.
Example — ad-hoc polymorphism
What happens when method signature is same and the return type is different?
The compiler will give error as the return value alone is not sufficient for the compiler to figure out which function it has to call.
Conceptual Aside — method signature
A method signature is a unique identification of a method for the C# compiler. The signature consists of a method name and the type and kind (value, reference, or output) of each of its formal parameters. Method signature does not include the return type.
Coercion polymorphism
The Coercion polymorphism is called as casting. This type of polymorphism occurs when an object or the primitive type value is cast into some other type. It could be either Implicit or Explicit. Implicit cast is done by compiler and explicit cast is done by us.
Assume that we have a function with argument int. If we call that function by passing a float value and if the run-time is able to convert the type and use it accordingly then it is coercion polymorphism.
Example — coercion polymorphism
double sum = 1 + 2.0;
Integer and floating-point arithmetic are totally different. Applying the plus operator to two operands of different types here is impossible without some form of coercion.
In this example, the integer and double types are automatically coerced (converted) to type double without an explicit cast. The entire expression is promoted to double. This is so because in Java we have primitive widening conversions. Why is it a polymorphism? It’s because the form of a data has changed to facilitate the operation.
Conceptual Asides
- Can we achieve polymorphism without inheritance?
Yes, method overloading is an example of that. In fact, two objects can be passed in a function for calling their function as long as that function shares same signature in both objects without inheritance but this is only supported in dynamically typed languages like JavaScript. - Is inheritance a type of polymorphism?
Inheritance is a dynamic form of polymorphism.
Thank you for reading this article. I hope this and its related articles will help you start your programming journey.
References:
Disclaimer & fair use statement:
This website may contain copyrighted material, the use of which may not have been specifically authorized by the copyright owner. This material is available in an effort to explain concept of Object Oriented Programming designs in an articulate and summarized manner to be used as an educational tool. The material contained in this website is distributed without profit for research and educational purposes. Only small portions of the original work are being used and those could not be used easily to duplicate the original work. This should constitute a ‘fair use’ of any such copyrighted material (referenced and provided for in section 107 of the US Copyright Law). If you wish to use any copyrighted material from this site for purposes of your own that go beyond ‘fair use’, you must obtain expressed permission from the copyright owner.