Type variables and reflection

The class Class whose objects represent information about Java classes is now a generic class that takes a type parameter. Thus, the type String is represented by an object of type Class<String>, type ETicket is represented by an object of type Class <ETicket> etc.

Unfortunately, type variables cannot be used in all contextss where a type name is expected. As we have seen, type variables can be used to specify the types of arguments and return values to functions and also to specify the types of variables within a function or a class. However in expressions such as

  if (s instanceof Shape){ ... }

we cannot replace Shape by a type variable and write

  if (s instanceof T){ ... } // T a type variable

This means that the following version of the function classequal (see page 83 in the older notes) would not work:

   public static <S,T> boolean classequal(S o1, T o2){
     return (o1 instanceof T) && (o2 instance of S); // Illegal!
   }

However, we can fruitfully exploit the fact that Class now has a type parameter. Suppose we want to write a reflective function that takes as input a class and creates a new object of that type. In the normal setup, we would have written:

   public Object createinstance(Class c){
      return c.newInstance();
   }

The return type of this function is Object because we cannot infer any information about the nature of the class c which is received as the argument.

In the revised framework, we if c is the Class object corresponding to a class T, then it is actually of type Class<T>. Thus, we can rewrite this function as follows:

   public T createinstance(Class<T> c){
      return c.newInstance();
   }

Now, notice that we are able to extract the underlying type T from c and use it to specify a more precise return value, thus avoiding the need to use a cast when calling this function.

One important point about Java's parameterized classes is that they are more sophisticated than macros or templates. It is not correct to think of a definition like

  public class LinkedList<T>{...}

as a skeleton that is instantiated into a ``real'' Java class each time it is actually used with a concrete value of T. There is truly only one class LinkedList<T> and all specific instantiations of LinkedList<T> have the same underlying object. In other words, if we declare

  LinkedList<String> stringlist = new LinkedList<String>();
  LinkedList<Integer> intlist = new LinkedList<Integer>();

then

  classequal(stringlist,intlist)

returns true.

This seems to have some unfortunate implications. For instance, suppose we write:

  LinkedList<String> sl = new LinkedList<String>();
  LinkedList<String> newsl = createinstance(sl.getClass());

Since all instantiations LinkedList<T> are of the same type, the value returned by sl.getClass() is of the ``generic'' type LinkedList<T> and the Java compiler is unable to reconcile this with the specific type LinkedList<String> defined for newsl. (Try this for yourself and see.)

Madhavan Mukund 2006-02-19