How to get the class of type variable in Java Generics
Solution 1
The only way is to store the class in an instance variable and require it as an argument of the constructor:
public class ContainerTest<T>
{
private Class<T> tClass;
public ContainerTest(Class<T> tClass) {
this.tCLass = tClass;
}
public void doSomething()
{
//access tClass here
}
}
Solution 2
If you are interested in the reflection way, I found a partial solution in this great article: http://www.artima.com/weblogs/viewpost.jsp?thread=208860
In short, you can use java.lang.Class.getGenericSuperclass()
and java.lang.reflect.ParameterizedType.getActualTypeArguments()
methods, but you have to subclass some parent super class.
Following snippet works for a class that directly extends the superclass AbstractUserType
. See the referenced article for more general solution.
import java.lang.reflect.ParameterizedType;
public class AbstractUserType<T> {
public Class<T> returnedClass() {
ParameterizedType parameterizedType = (ParameterizedType) getClass()
.getGenericSuperclass();
@SuppressWarnings("unchecked")
Class<T> ret = (Class<T>) parameterizedType.getActualTypeArguments()[0];
return ret;
}
public static void main(String[] args) {
AbstractUserType<String> myVar = new AbstractUserType<String>() {};
System.err.println(myVar.returnedClass());
}
}
Solution 3
There is no "clean" way to get the Generic Type argument from within the class. Instead, a common pattern is to pass the Class of the Generic Type to the constructor and keep it as an inner property juste as done in the java.util.EnumMap implementation.
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/EnumMap.html http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/EnumMap.java
public class ContainerTest<T> {
Class<T> type;
T t;
public ContainerTest(Class<T> type) {
this.type = type;
}
public void setT(T t) {
this.t = t;
}
public T getT() {
return t;
}
public void doSomething() {
//There you can use "type" property.
}
}
Solution 4
No. It is not possible because of type erasure (the type parameters are compiled as Object + type casts). If you really need to know/enforce the type in runtime you may store a reference to a Class
object.
public class ContainerTest<T> {
private final Class<T> klass;
private final List<T> list = new ArrayList<T>();
ContainerTest(Class<T> klass) {
this.klass = klass;
}
Class<T> getElementClass() {
return klass;
}
void add(T t) {
//klass.cast forces a runtime cast operation
list.add(klass.cast(t));
}
}
Use:
ContainerTest<String> c = new ContainerTest<>(String.class);
Solution 5
There is a way to get the runtime type of the type parameter by using Guava's TypeToken
to capture it. The solution's disadvantage is that you have to create an anonymous subclass each time you need an instance of Container
.
class Container<T> {
TypeToken<T> tokenOfContainedType = new TypeToken<T>(getClass()) {};
public Type getContainedType() {
return tokenOfContainedType.getType();
}
}
class TestCase {
// note that containerTest is not a simple instance of Container,
// an anonymous subclass is created
private Container<String> containerTest = new Container<String>() {};
@Test
public void test() {
Assert.assertEquals(String.class, containerTest.getContainedType());
}
}
The key of this solution is described in tha JavaDoc of TypeToken
's constructor used in the code above:
Clients create an empty anonymous subclass. Doing so embeds the type parameter in the anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure.
Related videos on Youtube
ZeDonDino
Updated on October 26, 2020Comments
-
ZeDonDino over 3 years
I've seen similar questions but they didnt help very much.
For instance I've got this Generic Class:
public class ContainerTest<T> { public void doSomething() { //I want here to determinate the Class of the type argument (In this case String) } }
and Another Class which uses this Container Class
public class TestCase { private ContainerTest<String> containerTest; public void someMethod() { containerTest.doSomething(); } }
Is it possible to determinate the Class of the type argument in method doSomething() without having an explicit type variable/field or any constructor in ContainerTest Class?
Update: Changed format of ContainerTest Class
-
vikingsteve about 11 years
if (t instanceof String)
? -
ZeDonDino about 11 yearsI've updated the question
-
Rohit Jain about 11 yearsCan't you pass the class type as an argument only?
-
Elgirhath about 4 yearsI might be late, but there is a great solution. Use C# (:
-
-
hertzsprung about 11 yearsThis is known as a 'type token'
-
ZeDonDino about 11 yearsI would like to invoke this method without any arguments.
-
ZeDonDino about 11 yearsUse this with caution, this is only supported In Java SE 7 and higher.
-
Javier about 11 yearsContainerTest<String> c = new ContainerTest<String>(String.class) //and so the diamond was born
-
Bob Wang about 11 yearsThis is exactly what I would do.
-
ZeDonDino about 11 yearsWell, I knew this. I hoped there is a way to get the Class out of the <String> and not passing the value into a constructor or field or similar
-
Renaud about 11 years@ZeDonDino I link EnumMap to show that even the OpenJDK is using this pattern in a standrad implementation.
-
zagyi about 11 yearsWow, a downvote, that's nice... Could you at least drop a comment on why you don't like it?
-
Didier L about 11 yearsThis is an interesting solution but I think it would be better suited for reflexion like guava does than for this question's use case. The problem with this solution is that it requires to create an anonymous class in each location where you instantiate it. (notice I am not the downvoter)
-
vach about 10 yearsAre you sure this is the only way?
-
Didier L about 10 yearsExcept if you force the caller to subclass your class, yes. But it is usually not a problem…