使用Java Reflections获取泛型类字段类型

时间:2015-09-25 13:42:21

标签: java generics reflection

我有以下类结构:

public class GenClass<T> {
    public T elem;
}

我以下列方式使用它:

public class Test {
    public GenClass<UUID> data;

现在我想使用数据的Field对象(Test.class.getField("data"))获取elem的类型 但是当我使用getType来检索类时,通用信息就会被删除。

如何将getGenericType中的通用信息映射到类对象以检索具有正确类型的字段?

编辑:由于存在一些误解,我试图澄清我的问题。 考虑这个例子:

public class AClass<T, Q> {
    public Q elem;
    // some other code using T...
}

public class BClass<T, Q> {
    public T elem;
    // some other code using Q...
}

现在我想要一个函数来获取elem的类:

public class Test {
    public AClass<UUID, String> a;
    public BClass<Integer, Float> b;

    void do() throws Exception {
        Field aField = Test.class.getField("a");
        Field bField = Test.class.getField("b");

        getType(aField, "elem"); // should return String.class
        getType(bField, "elem"); // should return Integer.class
    }

    Class<?> getType(Field f, String classField) {
        // ???
    } 
}

我如何实施getType以获得我想要的结果?

2 个答案:

答案 0 :(得分:1)

通过调用Type,您拥有与您的字段data对应的getGenericType对象。

Type t = f.getGenericType();  // f is your Field

Type interface及其实现代表了此处可能出现的类型的不同情况。由于data的类型为GenClass<UUID>,使用类型参数进行参数化,因此此处返回的Type实际上是ParameterizedType

ParameterizedType pt = (ParameterizedType) t;

通常可能有多个泛型类型参数,但这里只有一个。致电ParameterizedType's getActualTypeArguments method

Type parameter = pt.getActualTypeArguments()[0];

是的,我们有另一个Type实例,但是这个实例代表Field的泛型类型参数,而不是Field本身。由于您在Test课程中提供了班级类型,因此Type只是普通的Class - UUID.class

System.out.println(parameter instanceof Class);
System.out.println(parameter == UUID.class);

输出:

true
true

答案 1 :(得分:1)

好的,所以您需要做的是获取 a/b 字段原始类型的类型参数和 ParameterizedType 的实际类型参数。然后可以将原始类型参数与您感兴趣的 A/BClass 上的 elem 字段匹配,然后您可以使用它来解析/匹配实际类型(数组排列)。

public class TestGenerics {
    public AClass<UUID, String> a;
    public BClass<Integer, Float> b;

    void getFieldTypes() throws NoSuchFieldException, SecurityException {
        Field aField = TestGenerics.class.getField("a");
        Field bField = TestGenerics.class.getField("b");

        Class<?> aFieldElem = getType(aField, "elem"); // should return String.class
        Class<?> bFieldElem = getType(bField, "elem"); // should return Integer.class

        System.out.println("a field, elem field type: " + aFieldElem.getSimpleName());
        System.out.println("b field, elem field type: " + bFieldElem.getSimpleName());
    }

    Class<?> getType(final Field f, final String classField) throws NoSuchFieldException, SecurityException {
        Type genericType = f.getGenericType();
        //going to assume this. In reality you'd want to do an instanceof first
        ParameterizedType pt = (ParameterizedType) genericType;
        Class<?> rawType = (Class<?>) pt.getRawType();
        Type[] actualTypeParams = pt.getActualTypeArguments();
        TypeVariable<?>[] rawTypeParams = rawType.getTypeParameters();

        Field classFieldOnF = rawType.getField(classField);

        genericType = getResolvedType(classFieldOnF.getGenericType(), rawTypeParams, actualTypeParams);

        //same here, you'd need to do an instanceof first
        return (Class<?>) genericType;
    }

    private Type getResolvedType(final Type genericType, final Type[] rawTypeParams, final Type[] actualTypes) {
        for (int i = 0; i < rawTypeParams.length; i++) {
            if (genericType == rawTypeParams[i]) return actualTypes[i];
        }
        return genericType;
    }

    public static void main(final String[] args) throws NoSuchFieldException, SecurityException {
        TestGenerics test = new TestGenerics();
        test.getFieldTypes();
    }
}

输出为:

a field, elem field type: String
b field, elem field type: Integer