Summary
Type arguments to generic classes are not available for reflection at runtime - or are they? The type arguments for statically declared types can be discovered at runtime. A look at how to do this, and why you might want to.
Advertisement
Probably the most common complaint about generics in Java is that
they are not reified - there is not a way to know at runtime that a
List<String> is any different from a List<Long>.
I've gotten so used to this that I was quite surprised to run across Neil Gafter's work on
Super
Type Tokens. It turns out that while the JVM will not track the
actual type arguments for instances of a generic class, it does
track the actual type arguments for subclasses of generic
classes. In other words, while a new ArrayList<String>()
is really just a new ArrayList() at runtime, if a class
extends ArrayList<String>, then the JVM knows that
String is the actual type argument for
List's type parameter.
Super type tokens are a nice trick to distinguish between types which have the same raw type, but different
type arguments. What they don't easily provide, however, is an easy
means of discovering what the type argument for a generic type
parameter is. I recently ran across a situation where I wanted to do exactly
that while trying to write an abstract base class to take some
of the work out of implementing Hibernate's UserType
interface:
public interface UserType {
/**
* The class returned by <tt>nullSafeGet()</tt>.
*/ public Class returnedClass();
/**
* Retrieve an instance of the mapped class from a JDBC resultset.
*/ public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws SQLException;
/**
* Write an instance of the mapped class to a prepared statement.
*/ public void nullSafeSet(PreparedStatement st, Object value, int index) throws SQLException;
...
}
UserType is used to provide custom mappings between database values
and Java objects. For example, one might need a UserType implementation to map a textual
representation of dates (stored as VARCHARs) to the Date class.
Because Hibernate needs
to support Java 1.4, the UserType interface is not itself generic, but it makes plenty of sense
for a base class which implements it to take advantage of generics. In a reified world I might have something like:
public abstract class AbstractUserType<T> implements UserType {
public Class returnedClass() { return T.class; //not remotely legal in Java 5 or 6.
}
abstract public T nullSafeGet(ResultSet rs, String[] names, Object owner) throws SQLException;
abstract protected void set(PreparedStatement st, T value, int index) throws SQLException;
public void nullSafeSet(PreparedStatement st, Object value, int index) throws SQLException {
set(st, (T) value, index);
}
}
Unfortunately, because of type erasure, the "obvious" implementation of
returnedClass() doesn't work. Indeed, an often used pattern in generic
programming is to require the type argument class as a method parameter:
public abstract class AbstractUserType<T> implements UserType {
private Class<T> returnedClass; protected AbstractUserType(Class<T> returnedClass) { this.returnedClass = returnedClass;
} public Class returnedClass() { return returnedClass;
}
...
}
While this works, it does so at the cost of forcing clients to repeat themselves:
public class DateType extends AbstractUserType<Date> { // One for the money
public DateType() { super(Date.class); // Two for the show
}
...
}
It turns out, however, that even though we cannot access the type of T directly, we can
get at our current class, and use the new interfaces extending java.lang.reflect.Type
(introduced in Java 5) to get at what we need. A new method on
Class was introduced, getGenericSuperclass().
If the class's parent is a generic class, then this will return a
ParameterizedType.
The getActualTypeArguments() method on
ParameterizedType in turn provides an array of the actual
type arguments that were used in extending the parent class.
At first glance, then, it seems that the following ought to do the trick:
public abstract class AbstractUserType<T> implements UserType {
... public Class returnedClass {
ParameterizedType parameterizedType =
(ParameterizedType) getClass().getGenericSuperClass(); return (Class) parameterizedtype.getActualTypeArguments()[0];
}
...
}
Indeed, for a class that directly extends AbstractUserType (and
provides a non-array class for the type parameter), this works
well. However, in general, several problems can occur:
For a class extending AbstractUserType<int[]>, the result of the
call to getActualTypeArguments()[0] will be a
GenericArrayType, even though one might expect it
to be of type Class.
If Child extends AbstractUserType, and Grandchild
extends Child, then the type returned by
Grandchild.class.getGenericSuperClass() will be referencing Child, not
AbstractUserType, and hence any actual type arguments would be those provided by
Grandchild. Even worse, if Child is not itself a generic
class, then Grandchild.class.getGenericSuperClass() will
return Child.class, which is of type Class,
not ParameterizedType.
Given class declarations:
public class Child<S> extends AbstractUserType<T>{...}
public class GrandChild extends Child<Long>{...}
then
Child.class.getGenericSuperClass() will return a
ParameterizedType whose actual type argument is a
TypeVariable
representing the type parameter S. This type variable
(or one which is .equals() to it) will also be the sole
element of the array returned by
Grandchild.class.getTypeParameters(). To get the "actual actual"
type argument to AbstractUserType, it is necessary to link these two together.
That said, it is possible to accomplish what we want. The first step is to
provide a method which might have been polite for Sun to include in the Type
interface itself (as it stands, Type is strictly a marker interface):
/**
* Get the underlying class for a type, or null if the type is a variable type.
* @param type the type
* @return the underlying class
*/ public static Class<?> getClass(Type type) { if (type instanceof Class) { return (Class) type;
} else if (type instanceof ParameterizedType) { return getClass(((ParameterizedType) type).getRawType());
} else if (type instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) type).getGenericComponentType();
Class<?> componentClass = getClass(componentType); if (componentClass != null ) { return Array.newInstance(componentClass, 0).getClass();
} else { return null;
}
} else { return null;
}
}
Note that we basically "give up" if we hit an unbound type variable; since the goal here is to
find a class, we might as well.
The next step is a bit more involved. We need to look at the
actual type arguments provided to the super class of the class in question. If that super class is
the base class we are interested in, then we are done. Otherwise, we need to repeat this process.
However, the actual type arguments we have just looked at may themselves be used as actual type
arguments to the next class up the inheritance hierarchy. Unfortunately, Java will not track this
for us; we'll need to do it ourselves.
/**
* Get the actual type arguments a child class has used to extend a generic base class.
*
* @param baseClass the base class
* @param childClass the child class
* @return a list of the raw classes for the actual type arguments.
*/ public static <T> List<Class<?>> getTypeArguments(
Class<T> baseClass, Class<? extends T> childClass) {
Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
Type type = childClass;
// start walking up the inheritance hierarchy until we hit baseClass while (! getClass(type).equals(baseClass)) { if (type instanceof Class) {
// there is no useful information for us in raw types, so just keep going.
type = ((Class) type).getGenericSuperclass();
} else {
ParameterizedType parameterizedType = (ParameterizedType) type;
Class<?> rawType = (Class) parameterizedType.getRawType();
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
TypeVariable<?>[] typeParameters = rawType.getTypeParameters(); for (int i = 0; i < actualTypeArguments.length; i++) {
resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
}
if (!rawType.equals(baseClass)) {
type = rawType.getGenericSuperclass();
}
}
}
// finally, for each actual type argument provided to baseClass, determine (if possible)
// the raw class for that type argument.
Type[] actualTypeArguments; if (type instanceof Class) {
actualTypeArguments = ((Class) type).getTypeParameters();
} else {
actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
}
List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
// resolve types by chasing down type variables. for (Type baseType: actualTypeArguments) { while (resolvedTypes.containsKey(baseType)) {
baseType = resolvedTypes.get(baseType);
}
typeArgumentsAsClasses.add(getClass(baseType));
} return typeArgumentsAsClasses;
}
Finally, we can accomplish our original goal:
public abstract class AbstractUserType<T> implements UserType {
... public Class returnedClass { return getTypeArguments(AbstractUserType.class, getClass()).get(0);
}
...
}
While in this case, we are returning raw classes, other use cases might want to see the extended
type information for the actual type arguments.
Unfortunately, we cannot do this in the case where an actual type argument is a type variable.
For example, if the actual type argument for type parameter T is Long,
and we are trying to resolve List<T>, we cannot do so without creating a new
ParameterizedType instance for the type Long<T>.
Since the ParameterizedType implementation
provided by Sun is non-instantiable by mere mortals, this would require (re)implementing
ParameterizedType. However, since the algorithm for the hashCode method
for ParameterizedType is not documented, this cannot be safely accomplished.
In particular, it would not be possible to create one of Gafter's Super Type Tokens to represent
the actual type argument.
That limitation noted, this can still be useful, and not just for the motivating example above.
For example, one could extend ArrayList to get the dynamic return type of
toArray() right without help:
public abstract class TypeAwareArrayList<T> extends ArrayList<T> {
@Override public T[] toArray() { return toArray(
(T[]) Array.newInstance(
ReflectionUtils.getTypeArguments(TypeAwareArrayList.class, getClass()).get(0),
size()));
}
}
Note that TypeAwareArrayList is declared abstract.
This forces client code to extend it, if only trivially, so that the type information is available:
TypeAwareArrayList<String> stringList
= new TypeAwareArrayList<String>(){}; // notice the trivial anonymous inner class
...
String[] stringArray = stringList.toArray();
While the discussion above has been focused on subclasses, similar generics-aware reflection
can be done for methods and fields. As with classes, the main thing to remember is that while the
dynamic runtime typing of objects is ignorant of generic typing, the type arguments to statically
declared types can be discovered through reflection. Hopefully, Java 7 can "erase erasure", and get
rid of this frustrating distinction.
The goal here isn't so much to create a type token as it is to adhere to the DRY principal. The problem with the factory approach is the need to declare the type three times - once as a type argument, and once as the return type for the factory create statement, and once in the create statement body.
The weakness in supertype tokens you point out is that it's possible to loose the distinction between two types which differ only in their type arguments. However, this is moot when the sole goal is instance creation, since such instances are indistinguishable at runtime anyway.
I'm a big fan of DRY, but, in this case, just passing in the type as a parameter is starting to look pretty good! It's very clear, there isn't pages of utility code, and no need for an unusual "abstract" class where nothing is abstract, requiring an unusual anonymous empty inner class.
> The goal here isn't so much to create a type token as it > is to adhere to the DRY principal. The problem with the > factory approach is the need to declare the type three > times - once as a type argument, and once as the return > type for the factory create statement, and once in the > create statement body.
Sure, but you have had to declare parametric types in plenty of places as well. Doesn't seem much of a gain.
> The weakness in supertype tokens you point out is that > it's possible to loose the distinction between two types > which differ only in their type arguments. However, this > is moot when the sole goal is instance creation, since > such instances are indistinguishable at runtime anyway.
Just creating instances is a limited case, there is plenty of generic code that wants to act like a container. Neil gave an example in his blog that I used in mine.
Another alternative along the lines that you suggest is:
This is similar to using a factory method and similar to the checked versions of collections already in Java. None of these options are ideal.
I think that now people are used to generics it will be possible to break migration compatibility and reify generics. There are few non-generic libraries that I use now so it would be relatively easy task to re-compile these with a new compiler to reify them. Before J5 you would also have had to re-write them, obviously a bigger task.
I've just signed in to ask a question. In the java desktop forums there was a vague muttering of a swing 2.0 some time ago, and one of the proposals that i made, was to create a api amenable to method chaining, to which some other user replied that it was difficult to maintain such an api because code couldn't be reused by inheritance, without redefining the signatures of each and every void methods in itself and its subclasses to. My question is this: would runtime generics allow us to declare an api of the genre public this setSomething(T something) where this would be interpreted to be the concrete type of the class that calls the method (no interfaces allowed obviously)?
> I've just signed in to ask a question. In the java desktop > forums there was a vague muttering of a swing 2.0 some > time ago, and one of the proposals that i made, was to > create a api amenable to method chaining, to which some > other user replied that it was difficult to maintain such > an api because code couldn't be reused by inheritance, > without redefining the signatures of each and every void > methods in itself and its subclasses to. My question is > this: would runtime generics allow us to declare an api of > the genre > public this setSomething(T something) where this would be > interpreted to be the concrete type of the class that > calls the method (no interfaces allowed obviously)?
You post is really off topic, but here goes.
What you are talking about is self types, though the keyword this has been proposed for Java like in your example. Self types can currently be done with generics like this:
package selftypes;
importstatic java.lang.System.*;
publicclass Main {
publicstaticvoid main( final String[] notUsed ) {
final MySomething something = new MySomething();
final MySomething something2 = something.setSomething( something );
out.println( something2 );
}
abstractstaticclass Something< S extends Something< S > > {
S something;
S setSomething( final S something ) {
this.something = something;
@SuppressWarnings( "unchecked" ) final S self = (S) this;
return self;
}
}
staticclass MySomething extends Something< MySomething > {}
}
Enum and Comparable of examples of self typing in the Java standard library.
Whilst not knocking your code, it is very good, and agreeing with you that it would be nice if these methods were in the standard library. The code doesn't seem to help much. The example you give is:
TypeAwareArrayList< String > stringList = new TypeAwareArrayList< String >() {}; // notice the trivial anonymous inner class
The trivial anonymous inner class might not be realistic, e.g. suppose you want a constructor with arguments or suppose you need a serial version UID.
If the collections were modified to accept an optional element factory in their constructors, CollectionFactories.integer in the example below. And factories were added to the collection classes themeseves to allow creation that inferred type from their factory argument, called ni in example below. Then you could write:
List< Integer > l = ArrayList.ni( CollectionFactories.integer );
This would appear to satisfy the DRY principle you are striving for to the same extent, two mentions of the type, and be simpler than using the reflection on generics technique proposed both in coding the API and in use.
There is certainly no requirement that the child class be anonymous; one could instead write:
publicclass StringArrayList extends TypeAwareArrayList<String> {
// custom constructors, serial version UID, etc...
}
Indeed, if you are going to want a TypeAwareArrayList<String> frequently, this would be a prefered approach to anonymous inner classes, if only to avoid several copies of the "same" class.
Of course, a down side to this approach is the need to explicitely declare a child of TypeAwareArrayList for each parameter class of interest. If I'm not mistaken, your CollectionFactories approach has a similar issue, which presumably can also be addressed through annonymous inner classes when appropriate.
As for including something like what I wrote in the standard library, I would much prefer to see Sun just give us reified types.
> I'm a big fan of DRY, but, in this case, just passing in > the type as a parameter is starting to look pretty good! > It's very clear, there isn't pages of utility code, and > d no need for an unusual "abstract" class where nothing is > abstract, requiring an unusual anonymous empty inner class.
I'm very sympathetic to your point of view :). Would your opinion change if the pages of utility code were in an open source library?
> Is it also possible to get the parameter names for methods that have generics?
I'm pretty sure that it's not. In fact, if you look at the byte code for a generated class, the parameter names are not present unless the code was compiled with debugging info.
> There is certainly no requirement that the child class be > anonymous; one could instead write: > > ... > > Of course, a down side to this approach is the need to > explicitely declare a child of TypeAwareArrayList for each > parameter class of interest. If I'm not mistaken, your > CollectionFactories approach has a similar issue, which > presumably can also be addressed through annonymous inner > classes when appropriate.
Yes
> As for including something like what I wrote in the > standard library, I would much prefer to see Sun just give > us reified types.
Yes again - I would prefer reified generics to any of the options suggested
> I'm very sympathetic to your point of view :). Would your > opinion change if the pages of utility code were in an > open source library?
You code is cool Ian, don't get me wrong. Open Source would be nicer. But nicest is for Sun to do it. You identified a few places where you "can't quite" do what you need (e.g. ParameterizedType issues) - maybe if Sun just fixes them???
Also, I have hardly ever needed this feature. So requiring yet another open source jar file, plus some "tricky" code, is a high barrier for my uses.