Get type name for generic parameter of generic class [duplicate]
I have a small problem in java while using genericity. I have a class A
:
public class A<T>
In a method of A
, I need to get the type name of T
.
Is there a way to find the string s
using T
?
(If I create A<String> temp = new A<String>();
, I want to be able to get java.lang.String
at one point - I have to use genericity because one of my methods will have to return a List<T>
).
This seems quite easy but I do not see how to do it.
You can't do this in general because of type erasure - an instance of A<String>
doesn't know the type of T
. If you need it, one way is to use a type literal:
public class A<T>
{
private final Class<T> clazz;
public A<T>(Class<T> clazz)
{
this.clazz = clazz;
}
// Use clazz in here
}
Then:
A<String> x = new A<String>(String.class);
It's ugly, but that's what type erasure does :(
An alternative is to use something like Guice's TypeLiteral
. This works because the type argument used to specify a superclass isn't erased. So you can do:
A<String> a = new A<String>() {};
a
now refers to a subclass of A<String>
, so by getting a.getClass().getSuperClass()
you can eventually get back to String
. It's pretty horrible though.
You can get the name of the generics from the subclass. See this example. We Define a parent class like this:
public class GetTypeParent<T> {
protected String getGenericName()
{
return ((Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0]).getTypeName();
}
}
We then define its child class in this way:
public class GetTypeChild extends GetTypeParent<Integer> {
public static void main(String[] args) {
GetTypeChild getTypeChild = new GetTypeChild();
System.out.println(getTypeChild.getGenericName());
}
}
You can see that in the main method, or in any instance method, I am capable to get the name of the generics type, in this case the main will print: java.lang.Integer.
Short answer: Impossible.
Slightly longer answer: Once your code is compiled, the type parameters is discarded. Thus, Java cannot know what you set there. You could, however, pass the class in question to your object and operate on it:
public class Example<T> {
private final Class<T> clazz;
public Example(Class<T> clazz){
this.clazz = clazz;
}
...
}
As is normally the case, Apache has a solution for this one with TypeUtils:
https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/reflect/TypeUtils.html
A quick example from the above question:
TypeUtils.getTypeArguments(temp.getClass(), A.class).get(A.class.getTypeParameters()[0])
Disclaimer: I did not attempt building this first, but have used this utility in a similar fashion in the past.
Generics in Java are implemented by erasure, so no, you won't be able to get the name of the "type" which was used to create your generic collection at run-time. Also, why not just inspect the elements to know what type it belongs to?
If you're doing it in a subclass which has it's parent class defining the generic type, this is what worked for me:
// get generic type class name
String name = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].toString();
// then when you've got the name, you can make the Class<T> object
Class.forName(name.replace("class ", ""))
Reason why I couldn't do it with #getClass()
instead of #toString()
in the first snip is that I was always getting the "Class" class, which is useless to me.
精彩评论