… still it is possible to get hold of the actual type parameters of parameterized types. Even if classical reflection does not deliver type information, this type parameters can still be extremely useful. I have a few pieces of code (DAO, controllers, etc.) that are parameterized by class objects.

An example:

1
2
3
4
5
6
7
8
public class EntityFormController<T> extends FormController<T> {
  private Class<T> entityClass;

  public String create() {
      Object instance=getEntityClass().newInstance();
      ...
  }
}

So for every specific instance of the property entityClass must be initialized explicitely by implementing a proper constructor, using dependency injection, etc.

1
2
3
4
5
public class AddressFormController extends EntityFormController<Address> {
  public class AddressFormController() {
      super(Address.class);
  }
}

This is kind of violating the DRY principle. The actual type is already given through the type parameter. So I was trying to eliminate this few lines of code and come up with a solution that can analyze any parameterized type from a class declaration (super class hierarchy + interfaces).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
 * @author Martin Ahrer
 *
 */
public class GenericTypeArgumentExtractor {
  private final Class<?> candidateClass;

  public GenericTypeArgumentExtractor(Class<?> candidateClass) {
      super();
      this.candidateClass = candidateClass;
  }

  public Class<?>[] getGenericTypeArguments(Class<?> clazz) {
      // TODO: hopefully clazz.getGenericSuperclass() == clazz.getSuperclass()
      Type[] types = Arrays.copyOf(clazz.getGenericInterfaces(), clazz.getGenericInterfaces().length
              + (clazz.getGenericSuperclass() != null ? 1 : 0));
      if (clazz.getGenericSuperclass() != null) {
          types[types.length - 1] = clazz.getGenericSuperclass();
      }
      // Iterate all types from which the clazz inherits. i.e all generic
      // interfaces and the generic super class.
      for (Type type : types) {
          Class<?>[] arguments = getGenericTypeArguments(type);
          if (arguments != null) {
              return arguments;
          }
      }
      return null;
  }

  private Class<?>[] getGenericTypeArguments(Type type) {
      if (type instanceof ParameterizedType) {
          ParameterizedType parameterizedType = (ParameterizedType) type;
          if (isCandidate(parameterizedType)) {
              Type[] arguments = parameterizedType.getActualTypeArguments();
              Class<?>[] result = new Class<?>[arguments.length];
              for (int i = 0; i < result.length; i++) {
                  result[i] = (Class<?>) (arguments[i] instanceof ParameterizedType ? ((ParameterizedType) arguments[i])
                          .getRawType()
                          : arguments[i]);
              }
              return result;
          }
      } else if (type != null) {
          Class<?>[] result = getGenericTypeArguments((Class<?>) type);
          return result;
      }
      return null;
  }

  /**
  * Detects the type from which we want the type arguments. Can be overridden
  * in subclasses.
  *
  * @param type
  *            the parameterized type to check for.
  * @return true if type arguments of the passed type are reqeusted.
  */
  protected boolean isCandidate(ParameterizedType type) {
      return candidateClass.isAssignableFrom((Class<?>) type.getRawType());
  }
}

Using the above class for extracting type parameters would return an array containing the Address class object.

1
2
Class<?>[] genericTypeArguments = new GenericTypeArgumentExtractor(EntityFormController.class)
      .getGenericTypeArguments(AddressFormController.class);

Comments