Generics and Polymorphism

In the previous two articles related to Generics we learned about generics usage and concepts. In this article, let us focus on the aspect of polymorphism and how generics support polymorphism. In general, polymorphism applies to the base type of the collection. For example, the following code creates an ArrayList which can hold Integers. Note that in the following code the base type is List and sub type is ArrayList(since ArrayList is a sub class of List).

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

In the above code snippet, List and ArrayList are ‘base’ types and Integer is the generic type. Because ArrayList is a subtype of a List, we can assign an ArrayList to a List reference. However, the following is WRONG and will not work.

class Car { }

class Audi extends Car { } // Audi is sub class of Car

List<Car> myCars = new ArrayList<Audi>(); // Doesn’t work!

The above type of polymorphic usage is not allowed and will not work. Because the type of the variable declaration must be same as the type we pass to the actual object type. In other words, as in the above example, if the type is <Car>, then we can only assign generic type <Car> only. We cannot assign super type of sub type of Car.

Just to summarize, the following notations are all WRONG.

List<Object> myObjects = new ArrayList<String>();

// WRONG even though String is subtype of Object.

List<Number> someNumbers = new ArrayList<Integer>();

// WRONG even though Integer is subtype of Number.

The following are CORRECT, considering the fact that the generic type is same.

List<Employee> myEmployees = new ArrayList<Employee>();

List<Object> myObjects = new ArrayList<Object>();

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

The polymorphism applies only to the ‘base’ type (type of the collection class) and NOT to the generics type.

Generic Methods

To understand how generics works in the context of methods, consider the following Employee abstract class and two concrete classes PermEmployee and TempEmployee to represent permanent employee and temporary employee respectively.

abstract class Employee {

public abstract void doWork();

}

class PermEmployee extends Employee {

public void doWork() {

System.out.println(“Permanent employee working!”);

}

}

class TempEmployee extends Employee {

public void doWork() {

System.out.println(“Temporary employee working!”);

}

}

Considering the above Employee example, if we have a method which accepts list of employees and does some processing:

public class EmployeeProcessor {

public void processEmployee(List<Employee> employeeList) {

// some processing.

}

}

We can only invoke the processEmployee() method with list of generic type Employee ONLY and NOT with any generic sub type. The following will not work and gives us compilation errors.

List<Employee> employees = new ArrayList<PermEmployee>();

employees.add(new PermEmployee());

processEmployee(employees); // ERROR

The reason behind not allowing subtypes to be passed into a method that takes a collection of a super type is that, there is a possibility that we may add WRONG object. Since compiler has no way to prevent us from putting wrong subtype objects into collection of super type, in order to prevent the problems Java language designers restricted passing generic subtypes where generic super type is expected at compile time itself. Moreover, due to the ‘type erasure’, JVM cannot figure out which object has been passed until runtime.

However, we can directly add the subtype objects to the list which has generic super type.

So, if we have a list of employees as shown below:

List<Employee> employees = new ArrayList<Employee>();

employees.add(new PermEmployee()); // THIS IS VALID.

employees.add(new TempEmployee()); // THIS IS VALID.

We can add an instance of a subtype into a collection declared with a supertype.

Workaround

In some situations, we may have to work around above restrictions and we should be able to pass generic subtype where generic super type is expected. In such situations, we can indicate to the compiler to accept any generic sub type of the declared argument type by promising (yes ) that we’ll not be putting anything into the collection. We do this using wildcard syntax <?>

Let us change our processEmployee() method signature to cater for this need.

public void processEmployee(List<? extends Employee> employeeList) {

// some processing.

}

Using above code, we are forcing compiler to accept generic sub type in the place of generic super type and also we should not add anything to this list inside the method. If we try to add something to this list from within the method, our code will not be compiled. We are free to iterator through list and invoke methods on each retrieved object.

Another point to note that, the ‘extends’ keyword in the wildcard expression represents BOTH subclasses and interface implementations as well. We can either pass generic subtypes which extends another class or implements an interface.

So far we have seen how polymorphism works with generics and collections. In the next few articles, we’ll learn another interesting topic in Java. See you in next article!

Understand more about Generics and Polymorphism in Whizlabs OCPJP 6 Training Course.

About Aditi Malhotra

Aditi Malhotra is the Content Marketing Manager at Whizlabs. Having a Master in Journalism and Mass Communication, she helps businesses stop playing around with Content Marketing and start seeing tangible ROI. A writer by day and a reader by night, she is a fine blend of both reality and fantasy. Apart from her professional commitments, she is also endearing to publish a book authored by her very soon.
Scroll to Top