Simply:
public static class MyClass<T> {
// i don't want to keep an instance of T, if it is not necessary.
// and it is not nice, not neat.
// Or, let's say, the member are in the form of :
ArrayList<T> mArrayList = new ArrayList<T>();
// the problem of getting the generic type parameter is still present.
}
@Test
public final void test() {
MyClass<Integer> myObject = new MyClass<Integer>();
getParamType( myObject );
}
private static final <T> void getParamType(final MyClass<T> _myObject) {
System.out.println(_myObject.getClass().getTypeParameters()[0]); // T
System.out.println(((T) new Object()).getClass()); // class java.lang.Object
}
How to let the code print class java.lang.Integer
?
i know quite a few of stackoverflow threads are asking (and answering) about this. Yet they couldn't solve this question.
- i don't know why some need to call
getGenericSuperclass()
- as there is no inheritance involved in this simple case. - And i can't cast it to
ParameterizedType
as well.
.
System.out.println((ParameterizedType) _myObject.getClass());
// Compile Error: Cannot cast from Class<capture#11-of ? extends TreeTest2.MyClass> to ParameterizedType
System.out.println((ParameterizedType) _myObject.getClass().getGenericSuperclass());
// Runtime Exception: java.lang.ClassCastException
Based on @Thomas's guide, i have found a work-around way to get class java.lang.Integer
.
First, we create an anonymous (it need to be anonymous) sub-class of MyClass<T>
in the testing code. (Which is weird. Why it only support sub-classes?)
@Test
public final void test() {
MyClass<Integer> myObject = new MyClass<Integer>() {}; // Anonymous sub-class
getParamType( myObject );
}
Then we can use the getGenericSuperclass()
method to get a Type
then cast it to ParameterizedType
and afterwards uses getActualTypeArguments()
:
private static final <T> void getParamType(final MyClass<T> _myObject) {
System.out.println( ((ParameterizedType) _myObject.getClass().getGenericSuperclass()).getActualTypeArguments()[0] );
}
It perfectly prints class java.lang.Integer
.
This is not-so-good because the testing codes should simulate the actual situation, where users most likely won't keep creating meaningless sub-classes.
This approach is based on the idea of the TypeReference class. But i don't really know how to use it. I have tried class MyClass<T> extends TypeReference<T>
. But i still have to create sub-class of MyClass<T>
to have TypeReference.getType()
prints class java.lang.Integer
.
Please help, and thanks for any inputs, as the best approach is not here yet.
A further question based on the above workaround: Why only anonymous sub-class works?
public static class SubMyClass<T> extends MyClass<T>{}
@Test
public final void test() {
MyClass<Integer> myObject = new MyClass<Integer>() {}; // Anonymous sub-class
getParamType( myObject ); // class java.lang.Integer
MyClass<Integer> mySubObject = new SubMyClass<Integer>(); // named sub-class
getParamType( mySubObject ); // T
}
(MyClass
and getParamType()
unchanged.)
Java has a misguided feature called Type Erasure that specifically prevents you from doing that.
Generic parameter information does not exist at runtime.
Instead, you can manually accept a
Class<T>
.To learn the value of
T
you'll need to capture it in a type definition by subclassing MyClass:You can also do this with an anonymous class if you want:
To resolve the value of T, you can use TypeTools:
This is sort of difficult, because Java deliberately can't do that ("type erasure").
The work-around is called super type tokens. There are also some threads on SO about that (like this one or that one).
When you have a question like this, you should ask yourself, how would you do it without Generics? Because any Generics program can be converted into an equivalent program without Generics. (This conversion is called type erasure.) So if you cannot do it without Generics, you cannot do it with Generics either.
Your program without Generics looks like this: