Java Basics: Polymorphism

I’ve always considered Polymorphism to be one of the most difficult programming concepts to explain, even though it’s actually quite simple.
polymorphism in java oops concepts

The problem may lie in the person describing it.  All too often they try to repeat an explanation they’ve heard in the past, only to modify it with their own quips, anecdotes and descriptions.

My challenge to myself was to find a way to present the subject of Polymorphism in as simplest a way as possible, preferably with some examples written in Java.

According to Wikipedia, there are three types of Polymorphism although some say there are four.

Ad-hoc Polymorphism

With ad-hoc polymorphism, you can have multiple methods or functions in a class that all share the same name but accept different types of arguments.  You may also know ad-hoc polymorphism as “function overloading” or “operator overloading”.

The official Wikipedia entry states “In programming languagesad hoc polymorphism is a kind of polymorphism in which polymorphic functions can be applied to arguments of different types, because a polymorphic function can denote a number of distinct and potentially heterogeneous implementations depending on the type of argument(s) to which it is applied.”

Say you have a function called “add” which takes two numbers and adds them together (e.g. 1 + 2 = 3).  You can then add another function to the same class file called “add” which takes two Strings and concatenates them together (e.g. “one” + “two” = “onetwo”).

So the behavior of these methods change depending on what values they are given to execute against at runtime even though the behaviors look and are named exactly the same.

A quick example in Java:



Parametric Polymorphism

The same Wikipedia entry denotes “Using parametric polymorphism, a function or a data type can be written generically so that it can handle values identically without depending on their type.”

In Java, this concept is known as “generics”.  You may have used generics in your code without knowing that you were implementing parametric polymorphism!

Generics add stability to your code by making more of your bugs detectable at compile time.  By specifying the type of object needed for a function, you can limit the amount of unknown conditions that might creep into your application.  Because of this, parametric polymorphism can also be called “compile-time polymorphism”.

Here’s an quick example of generics and parametric polymorphism in Java:

In the above example, only objects that were instantiated from a class called “Animal” can be added to the animals List.  Anything else: Strings, Integers, Booleans, etc will cause a compilation error.


Subtype polymorphism, also known as “runtime polymorphism” is what many think of when they try to define polymorphism.   They often cite examples such as duck, cuckoo, and ostrich which are types of “birds”, or dogs, cats and birds are types of “animals”.  If an Animal can speak, it stands to reason by the rules of subtype polymorphism that any object derived from Animal should have the same ability to speak though the specific implementation may be different.  For instance, a cat may “meow” and a duck may “quack”.

Subtyping therefore is the process of creating a more granular version of the parent type that all share something in common:  the characteristics and behavior of the object they descended from, though they could be drastically different.  This is where the “many forms” definition of polymorphism came about and why subtype polymorphism is used most often in defining the general term polymorphism.

Based on the Animal reference above, an example in Java might appear something like this:

Coercion Polymorphism

Some say there is another form of polymorphism called Coercion Polymorphism.  Coercion or “casting” happens when an object or a primitive is cast into another object type or primitive type.

In this example, called up-casting, we create a new instance of a Cat but cast it to an instance of an Animal.  This is perfectly legal and safe since a Cat is derived from an Animal.  By safe, we mean that all characteristics of Animal are found in a Cat.  There is no chance, through casting or coercion polymorphism, that the Cat will obtain the ability to bark “Woof” for instance.  Anything an Animal can do, the Cat can do.

Based on our previous code, we could not actually instantiate an Animal in this case.  Animal is marked as abstract and therefore cannot be instantiated.  But what if we could?  Would we expect the output to be “Meow!”.  Unfortunately, because Animal has no direct implementation of the method talk(), we cannot expect this to work.  This is an example of down-casting, and there is no guarantee that the result will be what you might expect it to be.

Still, casting is a distant form of polymorphism.  It implies that we can perform functions due to inheritance but with down-casting, the output may result in error.

With all of these examples, we can see that “polymorphism” is exactly what it is defined to be:  an object that can undertake “many forms”.  You can use polymorphism to your advantage but without careful consideration this object-oriented power can lead to dangerous expectations.

About the Author: Erich Cervantez

1 Comment+ Add Comment

Leave a comment