Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reflection for Class of generic parameter in Java?

Imagine the following scenario:

class MyClass extends OtherClass<String>{

   String myName;
   //Whatever

}

class OtherClass<T> {

   T myfield;

}

And I am analyzing MyClass using reflection specifically (MyClass.class).getDeclaredFields(), in this case I will get the following fields (and Types, using getType() of the Field):

myName --> String
myField --> T

I want to get the actual Type for T, which is known at runtime due to the explicit "String" in the extends notation, how do I go about getting the non-genetic type of myField?

EDIT RESOLVED:

Seems like the answer is "you can't". For those who may look at this question later I'd recommend using Jackson (I was trying to do this to generate JSON) and annotating your classes and fields in such a way so that Jackson is aware of the inheritance hierarchy and can automatically do what the correct answer below suggested.

like image 502
Sam Stern Avatar asked Jun 22 '12 22:06

Sam Stern


2 Answers

This can be achieved with reflection only because you explicitly used String, otherwise this information would've been lost due to type erasure.

ParameterizedType t = (ParameterizedType) MyClass.class.getGenericSuperclass(); // OtherClass<String>
Class<?> clazz = (Class<?>) t.getActualTypeArguments()[0]; // Class<String>
like image 145
Jeffrey Avatar answered Nov 14 '22 13:11

Jeffrey


I found a nice explanation here:

When runtime inspecting a parameterizable type itself, like java.util.List, there is no way of knowing what type is has been parameterized to. This makes sense since the type can be parameterized to all kinds of types in the same application. But, when you inspect the method or field that declares the use of a parameterized type, you can see at runtime what type the paramerizable type was parameterized to.

In short:

You cannot see on a type itself what type it is parameterized to a runtime, but you can see it in fields and methods where it is used and parameterized.

In code:

You can't see T here:

class MyClass<T> { T myField; }

You can see the "T" here:

class FooClass {
    MyClass<? extends Serializable> fooField;
}

Here you would be able to tell the type and type parameters of fooField. See getGeneric*() methods of Class and Method.

By the way, I often see this (shortened):

Class fieldArgClass = (Class) aType.getActualTypeArguments()[0];

This is not correct, because getActualTypeArguments() may, and often will, return TypeVariable instead of class - that's when the generic is <? extends SomeClass> instead of just <SomeClass>. It can go deeper, imagine:

class FooClass {
    MyClass<? extends Map<String, List<? extends Serializable>>> fooField;
}

So you get a tree of Types. But that's a bit off-topic. Enjoy :)

like image 24
Ondra Žižka Avatar answered Nov 14 '22 14:11

Ondra Žižka