OOPs principle – Polymorphism – java example5 min read

Google’s definition – the condition occurring in several different forms.

Polymorphism is one of the core principles of the OOP’s and it is very important to have a good understanding of it. Before we go into Polymorphism, go through the Abstraction OOPs principle and Inheritance OOPs principle since it indirectly relates to it. Moreover, the examples we used there make more sense since we already used the polymorphism in those examples.

Now for the definition, polymorphism is just like google’s definitions and just think of those several different forms as an object.

OOPs definition – Polymorphism allows the objects to behave differently in certain cases or allows the action/operation to be accomplished in different ways.

Polymorphism can be achieved in two ways.

  • Method overloading – is where use the same method, but passes different parameters to the method and it is resolved at compile time. So it aka known as Compile time polymorphism
  • Method overriding – is where we use the same method, same signature, and same parameters (of course, in two different classes with relationship) but the correct method in the class is called upon based on the object creation at the run-time, So it is aka known as Run time polymorphism.

Simple real-world example

A simple real-world example is always a good way to capture/understand things quickly and easily. To understand the polymorphism, lets use our good old Dog class example.

  • Polymorphism by method overloading

There are two methods with the same name, but the same parameters for the method are varying, so it can be resolved at the compile time itself.

  • Polymorphism by method overriding

makeSound method having same method name, same parameters, same return type, but both resides in two different classes and that it going to be a hint for the compiler on how to resolve this at the runtime. Simply, the object which we created will determine which class makeSound() method should be called at the run time.

Lets put into example into action

package com.zingscoop.polymorphism;

public interface Animal {
    void makeSound();
}
package com.zingscoop.polymorphism;

public class Dog implements Animal {

    @Override
    public void makeSound() {
        System.out.println("bark !!");
    }

    public void makeSound(int noOfTimes) {
        for (int i = 1; i <= noOfTimes; i++) {
            System.out.print(i + " ");
            makeSound();
        }
    }
    
}
package com.zingscoop.polymorphism;

public class Cat implements Animal {

    @Override
    public void makeSound() {
        System.out.println("Meowing !!");
    }

    public void makeSound(int noOfTimes) {
        for (int i = 1; i <= noOfTimes; i++) {
            System.out.print(i + " ");
            makeSound();
        }
    }
    
}
package com.zingscoop.polymorphism;

public class PolymorphismAppTest {

    public static void main(String[] args) {
        System.out.println("Polymorphism my method overloading");
        Dog d = new Dog();
        d.makeSound();
        d.makeSound(2);

        System.out.println("Polymorphism my method overriding");

        Animal dog = new Dog();
        dog.makeSound();

        Animal cat = new Cat();
        cat.makeSound();


    }
    
}
Polymorphism my method overloading
bark !!
1 bark !!
2 bark !!
Polymorphism my method overriding
bark !!
Meowing !!

Final thoughts

  • It all sounds good and easy. Where do we use this Polymorphism? In java, we already use it in many places ex: ArrayList implements List. Let’s try to understand this bit more.
        List<Dog> listOfDogs = new ArrayList<Dog>(); // ArrayList class implements List 
        List<Animal> animals = listOfDogs; // We can't do this, compile time error.
        animals.add(new Cat()); //Why it is compiled then?
        Dog doggie = listOfDogs.get(0);

Now why there is a compile-time error in line no:2. Let’s understand it theoretically, I know Dog IS-A animal, then why can’t I just add a list of dogs to the list of animals list?

The answer is NO. You cannot do that because a List of animals can’t hold cats or dogs because it violates the type safety check and java needs to have its type determined at the runtime else there will be runTimeExcpetion. In the above code, lines no 2 and 3, litter pollutes the type safety, and we can’t determine whether the List<Animal> is holding a list of cats or dogs. Anyway, it can’t hold both. Please go through this java documentation on the erasure of Generic Types to understand java resolves the type check.

  • Producer extends and Consumer super

    Producer Extends – We have a List to produce T values and we need to read Ts from the list, then we gotta declare it with ? extends T.
    ex: List<? extends Animal>. But you cannot add to this list. (Now this explains why we can’t add a list of dogs to the list of Animal class)

    Consumer Super – We have a List to consume T values and we need to write Ts into the list as well, then we need to declare it with ? super T, e.g. List<? super Animal>. (Importation note: this doesn’t guarantee that the same type will be read from the list, it will come back as Object class)

There is another concept is called parametric polymorphism, which we will cover in a separate post.

All the above examples are available on github.

Stay subscribed. more to come. We will keep updating. Happy Scooping…