Java

Polymorphism in Java Generics

Since the early days as Java programmer we all know how to instantiate and use Collection objects. A List interface instantiated as a concrete class will look like the below.

List myArrayList  =  new ArrayList();

If myArrayList is supposed to hold only Integer objects then from Java 5 compiler onwards as per Java Generics specification that instantiation will look like this:

List<Integer> myArrayList = new ArrayList<Integer>();

In the same lines methods that accept and or return String lists will be modified from

public List processStrings(ArrayList myStringList);

to

public List<String> processStrings(ArrayList<String> myStringList);

And they are type safe so we do not have to typecast to retrieve the items of the list object

String aStringFromMyStringList = myStringList.get(0); //No ClassCastException possible.

The above will not compile if aStringFromMyStringList is declared anything other than String.

Till here we should be satisfied about how Object oriented Java worked, but the next item may surprise many.

When we are using List<Integer> myArrayList = new ArrayList<Integer>(); meant that we should be using “Integer” only in the ArrayList and NOTHING ELSE. Wait a minute, aren’t generics a part of OOP, means can’t we apply polymorphism in these objects? The answer is NO. Let’s see why.

We have already seen Polymorphism applies to the base type of collections and that is why List<Integer> myArrayList can be instantiated as new ArrayList<Integer>();

But what about this:

class Parent{}

class Child extends Parent{}

Using the above the following instantiation not work will and end up in Compilation Error.

List<Parent> myList = new ArrayList<Child>() //Compilation Error;

The simple rule is the type of the variable declaration must match the type you pass to the actual object type. If we declare List<Parent> myList then whatever I assign to myList MUST be exactly of type <Parent> only and not a subtype of Parent class not a supertype of Parent class.

That means the correct code is:

List<Parent> myList = new ArrayList<Parent>(); // Compiles fine

But the above contradicts with traditional java programmers who are accustomed to use the below which is legal.

Parent[] myParentArray = new Child[10];

To understand the above discrepancy in detail let’s have an inheritance structure as below:

public class Animal{}

public class Cat extends Animal{}

public class Dog extends Animal{}

We can implement Polymorphism in Arrays as arrays are not supposed to be type Safe. See the below example for arrays and why we need type safe lists as Collection objects.

public void addAnimals(Animal[] animals ) {
 	animals [0] = new Animal();

             // If passed animal[] is of type Dog[] then we are adding a Cat object to a Dog[] array.
 	animals [1] = new Cat();

             // If passed animal[] is of type Cat[] then we are adding a Dog object to a cat[] array.
             animals [1] = new Dog(); 
}

Since Cat or Dog is type of Animal hence Cat Array or a Dog Array can be passed as an Animal Array.

public class callerClass() {
              Animal[] animalArray = new Animal[10];
              Cat[] catArray = new Cat[10];
              Dog[] dogArray = new Dog[10];

             addAnimals(animalArray); //Expected, no questions raised here. 
 addAnimals(catArray); //As Cat[] is a type of Animal[] so we may end up in adding a Cat in Dog Array.                                                
             addAnimals(dogArray); // As Dog[] is a type of Animal[] so if Cat[] is passed we may end up in adding a Dog in a //Cat array.
}

But see what happens if we use Collections. We can have similar method like the above:

public void addAnimals(List<Animal> myAnimalList()) {     //Some code here.  }

A caller method which calls the above method will be like below.

public class callerClass() {
              List<Animal> animalList = new ArrayList<Animal>();
              List<Cat> catList = new ArrayList<Cat>();
              List<Dog> dogList = new ArrayList<Dog>();

             addAnimals(animalList); 
             addAnimals(catList);
             addAnimals(dogList); 
}

What happens if we try to compile the above? It will fail at line addAnimals(catList); and addAnimals(dogList), because the List type do not match with the expected list type of the addAnimals(List<Animal> myAnimalList()) method. The method expects list declared as Animal type ONLY.

Although the above failed, generics can actually HOLD instance of subtypes when a list is declared as a list of supertypes. For example we can have a detail implementation of the addAnimals(List<Animal> myAnimalList ()) method like below.

public void addAnimals(List<Animal> myAnimalList ()) {
           aList.add(new Animal()); // Expected code.
           aList.add(new Cat()); //Yes this works.
           aList.add(new Dog()); //Any Animal subtype works.
}

This means we can apply the sub-super class inheritance concept is ADDING objects to the list but not in assigning or passing objects as a method argument.

And this is the reason why Java prevents the addAnimals(catList) code to be compiled because if that gets compiled then later on in the implemented addAnimals method we can always have aList.add(new Dog()) code, even if aList is a type of Cat List, which is wrong! We cannot have a Dog object added to a Cat List because the list is declared to have Cat objects (or its sub classes) ONLY. Generics are there to make the Lists TYPE SAFE and technically meaningful. In order to accept polymorphic sub/super classes we can enhance the method signature by using wildcards, which may be discussed in another session.

Sudipta Mukhopadhyay

He is a Java/JEE developer for 17 years and works as consultant in Maryland, USA with interests in Enterprise Java techniques, Microservices, Cloud computing and technology trends.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Jorge
Jorge
9 years ago

Use like this example:

public class Generic {

public void test(){
arg(new ArrayList());
}

private void arg(List list){}

private interface Parent{}
private class Child implements Parent{}

}

Jorge
Jorge
9 years ago

This editor is escapeing the tag, so I have replaced it by “-“.

Use -? extends Parent- like this example:

public class Generic {

public void test(){
arg(new ArrayList-Child-());
}

private void arg(List-? extends Parent- list){}

private interface Parent{}
private class Child implements Parent{}

}

Back to top button