How do I get a class instance of generic type T?
I have a generics class, Foo<T>
. In a method of Foo
, I want to get the class instance of type T
, but I just can't call T.class
.
What is the preferred way to get around it using T.class
?
Solution 1:
The short answer is, that there is no way to find out the runtime type of generic type parameters in Java. I suggest reading the chapter about type erasure in the Java Tutorial for more details.
A popular solution to this is to pass the Class
of the type parameter into the constructor of the generic type, e.g.
class Foo<T> {
final Class<T> typeParameterClass;
public Foo(Class<T> typeParameterClass) {
this.typeParameterClass = typeParameterClass;
}
public void bar() {
// you can access the typeParameterClass here and do whatever you like
}
}
Solution 2:
I was looking for a way to do this myself without adding an extra dependency to the classpath. After some investigation I found that it is possible as long as you have a generic supertype. This was OK for me as I was working with a DAO layer with a generic layer supertype. If this fits your scenario then it's the neatest approach IMHO.
Most generics use cases I've come across have some kind of generic supertype e.g. List<T>
for ArrayList<T>
or GenericDAO<T>
for DAO<T>
, etc.
Pure Java solution
The article Accessing generic types at runtime in Java explains how you can do it using pure Java.
@SuppressWarnings("unchecked")
public GenericJpaDao() {
this.entityBeanType = ((Class) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0]);
}
Spring solution
My project was using Spring which is even better as Spring has a handy utility method for finding the type. This is the best approach for me as it looks neatest. I guess if you weren't using Spring you could write your own utility method.
import org.springframework.core.GenericTypeResolver;
public abstract class AbstractHibernateDao<T extends DomainObject> implements DataAccessObject<T>
{
@Autowired
private SessionFactory sessionFactory;
private final Class<T> genericType;
private final String RECORD_COUNT_HQL;
private final String FIND_ALL_HQL;
@SuppressWarnings("unchecked")
public AbstractHibernateDao()
{
this.genericType = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), AbstractHibernateDao.class);
this.RECORD_COUNT_HQL = "select count(*) from " + this.genericType.getName();
this.FIND_ALL_HQL = "from " + this.genericType.getName() + " t ";
}
Full code example
Some people are struggling in the comments to get this working so I wrote a small application to show both approaches in action. https://github.com/benthurley82/generic-type-resolver-test
Solution 3:
There is a small loophole however: if you define your Foo
class as abstract.
That would mean you have to instantiate you class as:
Foo<MyType> myFoo = new Foo<MyType>(){};
(Note the double braces at the end.)
Now you can retrieve the type of T
at runtime:
Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
Note however that mySuperclass
has to be the superclass of the class definition actually defining the final type for T
.
It is also not very elegant, but you have to decide whether you prefer new Foo<MyType>(){}
or new Foo<MyType>(MyType.class);
in your code.
For example:
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.NoSuchElementException;
/**
* Captures and silently ignores stack exceptions upon popping.
*/
public abstract class SilentStack<E> extends ArrayDeque<E> {
public E pop() {
try {
return super.pop();
}
catch( NoSuchElementException nsee ) {
return create();
}
}
public E create() {
try {
Type sooper = getClass().getGenericSuperclass();
Type t = ((ParameterizedType)sooper).getActualTypeArguments()[ 0 ];
return (E)(Class.forName( t.toString() ).newInstance());
}
catch( Exception e ) {
return null;
}
}
}
Then:
public class Main {
// Note the braces...
private Deque<String> stack = new SilentStack<String>(){};
public static void main( String args[] ) {
// Returns a new instance of String.
String s = stack.pop();
System.out.printf( "s = '%s'\n", s );
}
}
Solution 4:
A standard approach/workaround/solution is to add a class
object to the constructor(s), like:
public class Foo<T> {
private Class<T> type;
public Foo(Class<T> type) {
this.type = type;
}
public Class<T> getType() {
return type;
}
public T newInstance() {
return type.newInstance();
}
}
Solution 5:
Imagine you have an abstract superclass that is generic:
public abstract class Foo<? extends T> {}
And then you have a second class that extends Foo with a generic Bar that extends T:
public class Second extends Foo<Bar> {}
You can get the class Bar.class
in the Foo class by selecting the Type
(from bert bruynooghe answer) and infering it using Class
instance:
Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
//Parse it as String
String className = tType.toString().split(" ")[1];
Class clazz = Class.forName(className);
You have to note this operation is not ideal, so it is a good idea to cache the computed value to avoid multiple calculations on this. One of the typical uses is in generic DAO implementation.
The final implementation:
public abstract class Foo<T> {
private Class<T> inferedClass;
public Class<T> getGenericClass(){
if(inferedClass == null){
Type mySuperclass = getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
String className = tType.toString().split(" ")[1];
inferedClass = Class.forName(className);
}
return inferedClass;
}
}
The value returned is Bar.class when invoked from Foo class in other function or from Bar class.