Reputation: 143
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?
Upvotes: 4
Views: 4193
Reputation: 1590
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
Upvotes: 1
Reputation: 178333
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
Upvotes: 5