Skip to main content

Introspecting generified classes

Posted by kohsuke on December 16, 2008 at 9:20 AM PST

When building a library like JAXB, com4j, or args4j, which introspects on Java classes to perform some work, one often need to introspect generics properly. For example, JAXB handles List<String> and List<Integer> differently.

Before Java5, such libraries normally relied on a Class object to represent a type, but in the post-generics era, Class can no longer represent all possible types in Java (for example, there's no Class object for List<String>), so JDK added the java.lang.reflect.Type interface to do this.

The Type interface has 5 subtypes:
TypeVariable,
ParameterizedType,
GenericArrayType,
WildcardType, and finally our beloved Class. Those 4 interfaces do not really abstract anything — they are just tuples and they could have very well be concrete classes, but for whatever reason the designer must have felt like otherwise.

Anyway, any Java type can be represented in a tree structure of these instances. For example, List<? extends Number> would be: ParameterizedType(List.class,WildcardType(Number.class,null/*no upper bound*/)). Or for T[] where T is defined as T extends Foo, the tree would be GenericArrayType(t) where t=TypeVariable("T",Foo.class). This t also gives you information about where such generics is defined.

Note that there's a little overlap between Class and GenericArrayType. a type like String[] can be represented either as String[].class or GenericArrayType(String.class). Different JDK methods return different forms (I filed a bug on this but forgot which one), so your code should anticipate both patterns.

The java.lang.reflect package in Java was extended to return the Type information, in addition to Class. Those are named getGenericXYZ where their corresponding Class version was getXYZ.

OK, so that was the basic intro of the generics reflection in Java, and the following is my contribution.

When you start building some introspection code, you'll realize that you need some type arithmetics to get things done correctly. For example, one of the common operations you need is a type erasure, which essentially amounts to taking a Type and coerce it into Class. Or one often needs to get the parameterized base type representation — that is, say you have class Foo<T> implements List<List<T>> and given Foo<Foo<String>, find out that this type is assignable to List<List<Foo>> (JAXB does this and then do type erasure to determine how to map a collection.)

Unfortunately JDK doesn't come with these type arithmetic operations, so I wrote a library to do this. Its main entry point is the Types class, which gives you these operations.

In addition, this library has a default implementation of four sub-interfaces of Type, so that you can create literal Type objects. It also contains a visitor pattern implementation on Type for those of you who need to write more sophisticated type arithmetics.

The audience of this library is likely limited to library/framework developers, but I hope you find this useful.

Comments

Great. You are a committer now.

I'd probably be interested in taking a closer look. I have a few places where I have to simulate multi-dispatch type behavior and wanted to implement something that uses rules similar to java's static method binding, but extended to parameters.

Ah, so far I didn't need it, but that's another useful method. Would you be interested in implementing it? I'm happy to give you a commit access. You can first compute the erasure and then use getBaseClass(), then do unification to determines the result, so I don't think it's too hard.

This is awesome; I've been looking for an implementation of exactly the Types.getBaseClass(Class,Class) method for a framework I've been developing. Any plans to implement a Types.isAssignable(Type,Type) method (a la Class.isAssignableFrom(Class))?