javareflectionbytecode-manipulation

Resolve a TypeVariable


Say I have these classes:

class Container<T> {
  private List<T> list;
}

And I have an instance of Container, say

Container<?> instance = new Container<String>();

Is there a way to find out list's actual type parameter at run-time? I.e., that it's a List of String, rather than a List of java.lang.reflect.TypeVariable.

I've been looking at Java's reflection (field.getGenericType()), various libraries (Guava Reflect, Typetools, ClassMate, even ASM, etc.) but I can't figure it out.

I have no control over the Container type, so I can't add any fields to it.

I realize it may not be possible at all due to erasure, although I don't mind messing with bytecode if that makes it possible.

EDIT I over-simplified my initial question. I have an additional piece of information available; I also have this type:

class StringContainerContainer {
  private Container<String> container;
}

(And several more types like it, for different generic parameters.)

At run-time, I'm given the instance object mentioned above. With a little refactoring of my codebase, I can also get to the StringContainerContainer class (or whichever *ContainerContainer class is relevant), which I can use to get a java.lang.reflect.Field for container.


Solution

  • With your edit, yes, you can find out that String is the actual type parameter:

    Field field = StringContainerContainer.class.getDeclaredField("container");
    ParameterizedType gt = (ParameterizedType) field.getGenericType();
    Class<?> arg = (Class<?>) gt.getActualTypeArguments()[0];
    System.out.println(arg);
    

    I should point out that this identifies the type argument of the container field of StringContainerContainer. Given how Container is written, that also happens to be the type argument of its list field, but that field isn't directly examined.