2010-11-24

Reflecting generic type parameters in Java

If you one is introspecting an object class in Java, it was always a problem to be able to find out, what is the actual type of the generic type parameter for a class field or property.

Ok, yesterday Daniel and I ware listening to a presentation about EclipseLink from a guy from Oracle (or a company, that is working with Oracle, but this is not so important). The question about Generic Type erasure was in our minds all the time, when the guy showed how EclipseLink is generating adapter classes for his model classes. Ok, we asked about the issue, and he showed us how one can extract generic type parameter information for object fields using reflection methods. This approach will work only on the Field classes actually, but this is more then enough to be able to extract actual generic parameter types for class fields :)

Here is a simple example, that I made inspired by this information:

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.Map;

import org.junit.Before;
import org.junit.Test;

public class GenericTypeParameterDemo {

 static final class MyGenericType<e extends Number> {
  public Map<String, E> map;
 }
 
 @Before
 public void setUp() throws Exception {
 }
 
 @Test
 public void extractGenericParameters() throws Exception {
  Field field = MyGenericType.class.getDeclaredField("map");

  Type fieldGenericType = field.getGenericType();
  ParameterizedType parametrizedFieldType = (ParameterizedType) fieldGenericType;

  for (Type actualParameterType : parametrizedFieldType.getActualTypeArguments()) {
   if ( actualParameterType instanceof TypeVariable<?>) {
    Type[] bounds = TypeVariable.class.cast(actualParameterType).getBounds();
    System.out.println(actualParameterType.toString() + 
     " with bounds " + Arrays.toString(bounds) );
   } else {
    System.out.println(actualParameterType);
   }
  }
 }
}

It is really simple, as you can see on lines 25 and 26. The trick is to really check if the result of Field.getGenericType()
is actually an instance of ParameterizedType. One can look up also other children of Type interface, that can bring more details on the structure of your field types.