解析TypeVariable

时间:2016-01-07 21:58:32

标签: java reflection bytecode-manipulation

说我有这些课程:

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

我有一个Container的实例,比如说

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

有没有办法在运行时找出list的实际类型参数?即,它是 List String,而不是 List java.lang.reflect.TypeVariable。< / p>

我一直在关注Java的反思(field.getGenericType()),各种图书馆(Guava ReflectTypetoolsClassMate,甚至是{{3等等)但我无法弄清楚。

我无法控制Container类型,因此我无法为其添加任何字段。

我意识到由于擦除可能根本不可能,尽管我不介意弄乱字节码,如果可以的话。

编辑我过度简化了我的初步问题。我还有其他信息;我也有这种类型:

class StringContainerContainer {
  private Container<String> container;
}

(还有更多类似的类型,用于不同的通用参数。)

在运行时,我给出了上面提到的instance个对象。通过对我的代码库进行一些重构,我还可以访问StringContainerContainer类(或与*ContainerContainer类相关的类),我可以使用它来获取java.lang.reflect.Field container }。

2 个答案:

答案 0 :(得分:2)

由于类型擦除,你不能这样做。使用反射可以做的最多的事情是获取有关声明的类型变量的信息,而不是运行时实例绑定的实际类型。编译器抛弃了这些信息。

有关详情,请参阅tutorial on Type Erasure和/或section 4.6 of the Java Language Specification,其中讨论了类型擦除。

如果您需要有关实例类的运行时类型信息,那么您可以做的最好是要求在实例构造中传递Class对象:

class Container<T> {
    private List<T> list;
    private Class<T> type;

    public Container(Class<T> type) {
        this.type = type;
    }
}

答案 1 :(得分:2)

使用您的编辑,是的,您可以发现String是实际的类型参数:

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

我应该指出,这标识了container的{​​{1}}字段的类型参数。鉴于如何编写StringContainerContainer,它也恰好是其Container字段的类型参数,但不直接检查该字段。