Return to Home Page
      Blog     Consulting     Seminars     Calendar     Books     CD-ROMS     Newsletter     About     FAQ      Search
 

10-18-04 <T extends NonGenericType> and Class<T>

As I continue to study the mysteries of erasure-based generics in Java, I've had a few small epiphanies about bounds.

  1. Bounds are necessary when you want to go beyond "holder" generics to generics that perform operations upon objects of parameterized types.

  2. Bounds compensate for erasure. Since the type of a parameter is erased within a generic definition, you cannot know what legal operations exist for that type. By giving a bound, you only allow types satisfying that bound to pass into the generic. You can then safely perform operations satisfying that bound (and the compiler checks everything).

  3. When a generic with a bound is compiled, the parameterized type is replaced everywhere by the first bound. Successive bounds are replaced by casts.
Because of the last point, the class Bounds:
class PipeFitting {
  public void solder() {}
}
class FortyFive extends PipeFitting {}
class Ninety extends PipeFitting {}
class UJoint extends PipeFitting {}

public class Bounds<T extends PipeFitting> {
  private T fitting;
  public Bounds(T fitting) { set(fitting); }
  public void set(T fitting) { this.fitting = fitting; }
  public T get() { return fitting; }
  public void solder() { fitting.solder(); }
  public static void main(String[] args) {
    Bounds<PipeFitting> pf =
      new Bounds<PipeFitting>(new FortyFive());
  }
}
Becomes, after erasure:
public class Bounds {
  private PipeFitting fitting;
  public Bounds(PipeFitting pipefitting) {
    set(pipefitting);
  }
  public void set(PipeFitting pipefitting) {
    fitting = pipefitting;
  }
  public PipeFitting get() {
    return fitting;
  }
  public void solder() {
    fitting.solder();
  }
  public static void main(java.lang.String args[]) {
    Bounds bounds = new Bounds(new FortyFive());
  }
}
I actually compiled it and ran it through JAD to produce this output.

The important thing to note is that a class with a bound where the bound does not involve a type parameter is no different from a class that is not generic, but just uses arguments of that bound.

From this we could conclude that it never makes sense to create a generic class if you are going to use bounds that do not involve a type parameter. Although I discovered from Neal Gafter's comment (search for "mea culpa") that we cannot necessarily rely on the Java library sources to be good examples of coding style, they are nonetheless the most abundant examples of generic programming around. So I still think it's worth hunting through them to see what is done.

If I do a regular expression search for '<[A-Z]+ extends', there were lots and lots of generics of the form:

<T extends Foo>	returnType f(Class<T> token) {
  // ...
}

A non-parameterized type is used in the bound, but the only reason for that type is so that a Class<T> token can be passed in. This "class token" idiom is very common in the Java libraries (sometimes for partly reversing the effect of erasure by passing in the exact class type).

Class<T> is new in Java 5. What does it mean? Previously, if we wanted a reference to a class object, we used Class. This is analogous to the way that Object points to any kind of object – a Class points to any kind of class object. So an initial guess as to the meaning of Class<T> is that it would allow you to point to a T.class or any subtype of T.class. But that doesn't work:

Class<Number> cn = Integer.class; // Won't compile.

Class<T> will only point to the exact type of T, not a subclass. This fits with the reason for the existence of wildcards, and we are in fact able to use wildcards here:

Class<? extends Number> cn = Integer.class; // Compiles.
And we can also revert to the meaning "points to any class" using an unbounded wildcard:
Class<?> cn = Integer.class; // OK.

So Class<T> is like a single-object generic holder class that only holds class references. When we need to pass class tokens, Class allows us to pass a reference to any class, whereas Class<T> performs a compile-time check to ensure that a reference of a particular class object is passed in. So we have compile-time safety for passing in class tokens. The idiom looks like this (for a generic method):

public static <T extends Foo> void f(Class<T> token) {}

This restricts the method so that it can only be used with Foo or a subclass, and it forces the token to be of the exact type of the parameter. Actually, in the case of a generic method, type inference takes over and the method is instantiated for whatever type you pass in as the class object. That is, you just say f(Foo.class) and the parameter type is inferred for you.

The key thing I'm trying to point out here is that Class<T> seems to be used in a very narrow, idiomatic way; together with <T extends Foo> (where Foo does not involve a type parameter, but is a specific class): simply to provide compile-time type checking for passing in a class token. And I'm not attacking static type checking here, I'm simply pointing out the idiom so that you can recognize what it means when you see it. (And I'm putting it up as a conjecture in this article to see if anyone has any other observations or interpretations).

Consider an example from the pre-Java 5 and post Java 5 libraries. Here is AWTEventMulticaster.getListeners() in Java 1.4:

public static EventListener[]
getListeners(EventListener l, Class listenerType) {
  int n = getListenerCount(l, listenerType);
  EventListener[] result =
    (EventListener[])Array.newInstance(listenerType, n);
  populateListenerArray(result, l, 0);
  return result;
}

Here, I can only pass in an EventListener (or subtype), but I can pass in absolutely any kind of Class object.

Here's the same code in Java 5:

public static <T extends EventListener> T[]
getListeners(EventListener l, Class<T> listenerType) {
  int n = getListenerCount(l, listenerType);
  T[] result = (T[])Array.newInstance(listenerType, n);
  populateListenerArray(result, l, 0);
  return result;
}

When you call this method, listenerType can be the class EventListener or a subclass, and the return type T[] will be of the same class as listenerType. So the idiom of <T extends NonGenericType> coupled with Class<T> (which seems to be the only appropriate way to apply <T extends NonGenericType>) is not a way to apply a method across more types, but instead a way to perform more precise static type checking.

In searching through the standard library code base, I also found quite a number of Class<? extends Foo>, for cases where the method wanted to allow all the subclass objects as well. Again, this provides better static type checking than just using a Class reference.

Feedback Wiki Page

    Links I Read
Cafe Au Lait
Artima
Daily Python URL
Martin Fowler
Joel on Software
Paul Graham
Cringely
Search     Home     Web Log     Articles     Calendar     Books     CD-ROMS     Seminars     Services     Newsletter     About     Contact     Site Feedback     Site Design     Server Maintenance     Powered by Zope
©2003 MindView, Inc.