You got Reflection in my Generics! You got Generics in my Reflection!

I like reflection. I like generics. I just hate it when they both come to the same party.

I use reflection a lot, especially for framework code. It’s a great way to avoid big, long blocks of code where you get something from one object or bean and assign it to another object or bean. Reflection and I have been friends for a long, long time.

I’ve also grown to really enjoy working with generics — I enjoy knowing, for example, what kind of “thing” is inside my List or Map. I like being able to avoid explicit casts. These are good things.

My big disappointment in life, however, is that generics are, for the most part, a purely compile-time construct.

Consider the following:

public class MyClass {
  private List<String> names;

  public List<String> getNames() {
    return this.names;
  }
  public void setNames(List<String> names) {
    this.names = names;
  }
}

Here’s a pretty straight-forward class, on which one can use reflection or bean introspection. But there are limitations. Suppose I try the following:

PropertyDescriptor descriptor = PropertyUtils.getPropertyDescriptor(new MyClass(), "names");
System.out.println(descriptor.getPropertyType());

What do you think you get in this case? Well, it turns out that the property is of type java.util.List. No information about the type in the angle brackets.

The worst case is something like this:

System.out.println(new ArrayList<String>().getClass());

In this example, there is no way to use reflection to find out anything about what’s in the angle brackets. The information that the list is a list of Strings is only used at compile-time, and that information is completely thrown away once the code is compiled. The .classno longer has any information about angle brackets.

This differs from other implementations of “type polymorphism” — in C++, for example, we use something called “templates”, which cause the creation of a completely different “type” for every different “parameter” that we might use. But Java is not C++. (David adds, “thankfully”).

Now, although mostof the information about the parameters of generic types is discarded, there are two cases where the information is retained at runtime:

  1. We can get the parameter information from any type used in a method signature.
  2. We can get the parameter information from any superclass definition.

Here are those two situations:

public List<String> getNames() {
  return this.names;
}

and:

public class SpecificWidgetMangler extends WidgetMangler<MyWidget> {
  ...
}

Now, if I want to build some framework code to help me populate some complicated classes that use generics, I have to do some really hard thinking. This isn’t just an abstract problem, I actually have exactly this problem in an HL7-related project. HL7 likes to define message “types” that map very nicely to generics. One might see something like this, for example, when defining a “Get Person Demographics Query” message:

public class GetPersonDemographicsQuery extends
  MCCI_MT002100CA.Message<MFMI_MT700751CA.ControlActEvent<PRPA_MT101101CA.ParameterList>> {

  ...
}

Hm. Generics nesting. That’s annoying.

So, let’s stick our nose into the ugliness of generics and reflection. Let’s consider, first, the case of the method with generic parts.

The following code can be used to get information about property-based generics:

Method readMethod = MyClass.class.getMethod("getNames", new Class[0]);
Type type = readMethod.getGenericReturnType();

This part is a bit hard to interpret, because this magic Type type is a bit opaque and hard to make sense of. But, basically, there are three types of generic Types:

  1. Classes
  2. ParameterizedTypes — our List<String>is one such example.
  3. TypeParameter— such as
    public <T> T createInstance(Class<T> c);

And, really, there’s no good way to use these, except to downcast:

Method readMethod = MyClass.class.getMethod("getNames", new Class[0]);
ParameterizedType type = (ParameterizedType) readMethod.getGenericReturnType();
for (Type argumentType : type.getActualTypeArguments()) {
  System.out.println(argumentType);
}

This should print out “class java.lang.String”. Once again, notice that the type isn’t guaranteed to be a Classbecause we could have any of these:

public List<T> getNames() {...

or

public List<Set<String>> getNames() {...

These situations can make a programmer go a bit cross-eyed. Especially if you want to build a framework like Hibernate — something that tries to automatically populate beans of the right type in a variety of situations.

It canbe done, so long as you have a root type that always knows how to resolve all parameterized types along the way. The reflection code is quite a bit trickier than most examples of reflection that you see. For example, the reflection utilities in Apache Commons BeanUtils require only a class (or an instance of that class) to perform most types of reflection. A reflection-based framework that’s generics-aware always needs to keep track of this root type as a “context” in which the generics can be resolved to actual types.

In our HL7 framework, for example, we always have a root message type that knows the actual types that fill in the template variables (all those things we so often call ‘T’).

For example, we might do something like this:

Document xml = getHl7MessageAsXmlDocument();
GetPersonDemographicsQuery query = (GetPersonDemographicsQuery) new XmlToJava().parse(xml);

The XmlToJavautility knows how to find the Java representation of the HL7 message, recurse through the Java representation (including being able to resolve generics) and instantiate child objects as necessary to represent the original HL7 message as Java objects. Recall our Get Person Demographics Query class:

public class GetPersonDemographicsQuery extends
  MCCI_MT002100CA.Message<MFMI_MT700751CA.ControlActEvent<PRPA_MT101101CA.ParameterList>> {

  ...
}

Message has one template parameter, representing the “control act” (in HL7-speak), and the ControlActEventhas one template parameter representing the message “payload”.

This is a good solution for us — the use of generics and a custom code generator results in considerably less application code than we’d get if we’d used, say, JAXB. But the framework to support it has had to deal with a number of hard problems, and unless developers have been exposed to the weirdnesses of generics and erasure, it’s a bit hard to explain why some of those problems are so hard.

This post has been brought to you with the able assistance of David Harcombe and Jason Roberts.

It's only fair to share...
Share on FacebookGoogle+Tweet about this on TwitterShare on LinkedIn

Leave a Reply