9 FEBRUARY, 2005
Delving further with Java generics

By John Hunt

Introduction
In an earlier article, we looked at the basics of the new Generics language features added to Java with Java 2 Platform Standard Edition 5.0 Development Kit (aka JDK 5.0). However, we stopped short of delving down into some of the more esoteric looking aspects of Java Generics. Nevertheless, although the language features discussed here might look somewhat unusual, they have a very important part to play in the real world benefits that generics can offer.

In this article, we will look at the relationship between generic lists of one-type and generic lists of another type of Java object. We will consider how they are and are not related and what the implications are for developers. This will lead us onto consider the use of wild cards and later bounded wildcards for generic variables.

Generics and their types
In the last article, we looked at how we can use generics with collections. We noted that generics allow a particular collection to be limited to a particular type of object. For example, we were able to write:

List<String> list = new ArrayList<String>();

This created an instance of the class ArrayList that is limited to hold only string objects. Thus later on when we retrieve elements within the array list we (and Java) will know that they are strings. For example:

Iterator<String> it = list.iterator();
while (it.hasNext()) {
      String st = it.next();
      System.out.println(st);
}

We also noted that what is created when you make an instance of a collection using generics is an instance of the appropriate concrete class. Thus, if we consider the following:

List<Integer> intList = new ArrayList<Integer>();
List<String> stringList = new ArrayList<String>();

Both the instances created are instances of the class ArrayList, it is just that intList is configured to hold instances of the class Integer and stringList is configured to hold instances of the class String. All well and good, but what about the following:

List<String> stringList = new ArrayList<String>();
List<Object> objectList = stringList;

What is the result of the above? Well at first sight, this may appear completely legal. After all what are we storing into the variable objectList? In practical terms, we are storing a reference to an instance of ArrayList. ArrayList is a class that implements the List interface and thus a variable of type List should be able to reference an instance of type ArrayList! Also, if we take this further, a String is a type of Object – remember Java is a single rooted class hierarchy language and the class Object is the root of all class hierarchies thus a String inherits from Object!

Of course, the above sounds quite intuitive but is sadly misleading. The statement:

List<Object> objectList = stringList;

Is very dangerous and is not something we would want the language to allow and indeed Java does not allow it. This statement would in fact lead a compiler error:

source>javac GenericsExample.java
GenericsExample.java:11: incompatible types
found   : java.util.List<java.lang.String>
required: java.util.List<java.lang.Object>
          List<Object> objectList = stringList;
                                    ^
1 error

So, the compiler has specified that objectList and stringList are incompatible types and I have said that we really don’t want to allow them to be the same. So, what is going on and why is it happening?

The answer comes when you think about what might be written if we were allowed to assign stringList to objectList:

Object obj = new Object();
objectList.add(obj);
String st = stringList.get(0);

This would allow us to add a new instance of the object class to the list referenced by objectList (which is of course the same list as is referenced by stringList). The net effect would be that stringList now held an object and not a string and thus the last line above would be illegal.

To avoid this situation from occurring, you cannot assign a generic collection of one type to a variable for a generic collection of another type (even if the two types are related by inheritance). This may go against all that we hold dear regarding classes, interfaces and inheritance but it makes practical sense.

Generics and Wildcards
Okay you may say – but what happens if I want to pass a generic collection into a method? For example, pre-JDK 5.0 I might well have written:

import java.util.Iterator;
import java.util.List;

public class EmployeeListExample {
    public void printPayslips(List employees) {
        Iterator it = employees.iterator();
        while (it.hasNext()) {
            printPayslip((Employee)it.next());
        }
    }
}

This code takes a list that contains Employee objects (or instances of subclasses of Employee). It iterates over this list passing each employee to the printPayslip method.

In JDK 5.0, I might want to modify this to use generics (then I can guarantee the contents of the list). For example, I might want to write the following:

import java.util.Iterator;
import java.util.List;

public class EmployeeListExample {
    public void printPayslips(List<Employee> employees) {
       Iterator<Employee> it = employees.iterator();
       while (it.hasNext()) {
           printPayslip((it.next());
       }
    }
}

This will work fine given what we know about generics – that is, as long as we pass it a list that has been instantiated to hold Employees. However, what about if we have a list of Managers (and we assume that Managers are a subclass of Employee)?

We already know that a List instantiated with the Employee class is not equal to a list instantiated with the Manager class (even if Manager is a direct subclass of Employee). Thus, we cannot pass in a Manager List to the printPayslips method. This seems something of a retrograde step, compared to the non-generic version. In the original version, we could pass in any type of list holding any type of object (even if this caused us problems later on).

However, there is a way around this – we can use a wildcard. That is, we do not specify at compile time the type of class used to instantiate the generic list. Instead, we allow it to be determined at runtime. This is written as:

List<?>

This is read as a “List of Unknown” type. The revised version of the printPayslips method would now resemble:

public void printPayslips(List<?> employees) {
        Iterator<?> it = employees.iterator();
        while (it.hasNext()) {
            printPayslip(it.next());
        }
}

Note however, that the iterator must also be specified with the wildcard. Now we can pass in an ArrayList of Managers. Note, however, that the type of the object returned from the it.next() method would be object. Thus if the printPayslip() method takes an Employee, then a cast will be needed.

Bounded Wildcards
The example above helps allow any type of generic list to be given to a printPaySlips method. However, if we examine this code and compare it with the non-generic version we find that we have actually undone everything we wanted generics to do. That is, we now allow any type of generic list to be given to the printPayslips method (it can contain employees, Strings, or indeed any type of object). The object returned from it.next() now has a type of object and we would need to cast it to Employee if printPayslip took an Employee (or subclass of Employee).

So, what is the use of this other than to make this method work with generics?

Actually, the point is we can now consider what are referred to as Bounded Wildcards. This version of a wild card allows us to specify that any collection or list instantiated for a particular type or subtype may be passed in. Thus, we can say that a list of employees or any subclass of employees may be passed into the printPayslips method. This version of the method now looks like this:

import java.util.Iterator;
import java.util.List;

public class EmployeeListExample {
    public void printPayslips(List<? extends Employee> employees) {
        Iterator<? extends Employee> it = employees.iterator();
        while (it.hasNext()) {
            printPayslip(it.next());
        }
    }

    public void printPayslip(Employee emp) {
        System.out.println(emp);
    }
}

We now have a method that can take a list of Employees or Managers and will return an object of type Employee from the it.next() method (even if it is actually a subclass of Employee). We have regained the type safe advantages of generics without tying the implementation to a particular type of class!




   


Get 6 FREE copies of "Application Development Advisor" magazine. This offer is open to IT professionals based in the United Kingdom. Coverage of .NET, XML and databases by experts.
www.appdevadvisor.co.uk
Visit Solutions Architect, our new website covering service-oriented infrastructures. Read articles and sign-up to receive the weekly emails.
www.solutionsarchitect.co.uk
Visit RFID Today, our new website covering Radio Frequency Identification. Read the latest articles and case studies. Sign-up to receive RFID Today magazine.
www.rfidtoday.co.uk

ADA Communications, Charwell House,
Wilsom Road, Alton, Hampshire, GU34 2PP, UK.
Tel: +44 (0)1420 594200
www.adacom.co.uk

© Copyright 2001 - 2005 by ADA Communications Ltd. All rights reserved. Statements of opinion and fact are made on the responsibility of the authors alone and do not imply an opinion on the part of ADA Communications Ltd or the editorial staff. Registered in England No. 04843018