4

I have the following class structure:

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

I use it in the following way:

public class Test {
    public GenClass<UUID> data;

Now I want to get the type of elem using the Field object of data(Test.class.getField("data")) But when I use getType to retrieve the class the Generic information is stripped away.

How can I map the generic Information from getGenericType to the class object to retrieve the field with a correct type?

Edit: Since there are some misunderstandings, I try to clarify my problem. Consider this example:

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...
}

Now I want a function to get the class of 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) {
        // ???
    } 
}

How did I need to implement getType to get my desired result?

2
  • 1
    Check stackoverflow.com/questions/1901164/… Commented Sep 25, 2015 at 14:04
  • But I don't know, which of the actual type arguments for the field is used for elem. Maybe I have a class like AClass<T, Q, B> and then declare B data. How do I know, that data is the third type argument? Commented Sep 25, 2015 at 14:26

2 Answers 2

5

You have the Type object corresponding to your field data, from calling getGenericType.

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

The Type interface and its implementations represent different cases of what kinds of types could be present here. Because data's type is GenClass<UUID>, parameterized with a type parameter, the Type returned here is actually a ParameterizedType.

ParameterizedType pt = (ParameterizedType) t;

Generally there could be multiple generic type parameters, but you have only one here. Call ParameterizedType's getActualTypeArguments method.

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

Yes, we have another Type instance, but this one represents the generic type parameter of the Field, not the Field itself. Because you supplied a class type in the Test class, this Type is nothing other than an ordinary Class -- UUID.class.

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

Output:

true
true
Sign up to request clarification or add additional context in comments.

1 Comment

Look at my edited question for a better explanation of my problem!
1

OK, so what you need to do is get both the type parameters on the raw type of your a/b field, and the actual type parameters on the ParameterizedType. The raw type Parameter can then be matched up with the elem field on the A/BClass you're interested in, but then you use that to resolve/match to the the actual type (the arrays line up).

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();
    }
}

Output is:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.