Social Icons

twitterfacebookgoogle pluslinkedinrss feedemail

Thursday, April 26, 2012

Java Generics and Type Erasure

I recently came across a situation at work where knowing the generic type on a field was needed and it turns out that my initial understanding of Java Generics was not correct. Java Generics provide compile-time type safety and my understanding was that these types were erased after compilation and were not accessible at run-time. It turns out, this only true at the class level but generics are not erased for fields and methods.

Here's an example, first, let's define a simple class:

import java.util.List;

public class GenericsTest {

  public GenericsTest() {
    this.stringList = null;
    this.stringField = null;
  }

  private List<String> stringList;
  private String stringField;
}

And now for our test:

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class FieldGenericsTest {

  public static void main(String[] args) throws Exception {

    GenericsTest gt = new GenericsTest();

    Field[] fields = gt.getClass().getDeclaredFields();
    for (Field field : fields) {

      // needed to access non-public fields
      field.setAccessible(true);

      Type fieldType = field.getType(); // will be List without generics
      System.out.println("field type: " + fieldType);

      Type genericType = field.getGenericType();
      if (genericType instanceof ParameterizedType) {

        ParameterizedType pt = (ParameterizedType) genericType;
        Type[] types = pt.getActualTypeArguments();

        // since we only have one generic type in our example, that is,
        // List<String> contains only one generic type parameter, the
        // size of this array will only be one.
        Type type = types[0];
        if (type.equals(String.class)) {
          System.out.println("generic type is string: " + type);
        }
      }
      else {

        // fields without generics will fall here
        System.out.println("not a parameterized type: " + genericType);
      }
    }
  }
}

This, by itself, is pretty powerful for the use cases where the generic type needs to be known at run-time. But, what if you have multiple fields with the same type/generic signature? That is, we have two fields with the type List<String>. We can use annotations to distinguish these fields apart and then access the annotations while we're iterating over the fields. My next post will deal with that use case.