Вопрос-ответ

How do I get a class instance of generic type T?

Как мне получить экземпляр класса универсального типа T?

У меня есть класс generics, Foo<T>. В методе Foo я хочу получить экземпляр класса типа T, но я просто не могу вызвать T.class.

Какой предпочтительный способ обойти это с помощью T.class?

Переведено автоматически
Ответ 1

Короткий ответ заключается в том, что в Java нет способа узнать тип среды выполнения по параметрам универсального типа. Я предлагаю прочитать главу об удалении типов в Руководстве по Java для получения более подробной информации.

Популярным решением этого является передача Class параметра типа в конструктор универсального типа, например

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
}
}
Ответ 2

Я искал способ сделать это самостоятельно, не добавляя дополнительную зависимость к classpath. После некоторого исследования я обнаружил, что это возможно, если у вас есть универсальный супертип с неродовым подклассом. Для меня это было нормально, поскольку я работал со слоем DAO с универсальным супертипом layer. Если это соответствует вашему сценарию, то, ИМХО, это самый аккуратный подход.

Большинство универсальных вариантов использования, с которыми я сталкивался, имеют какой-то универсальный супертип, например, List<T> for ArrayList<T> или GenericDAO<T> for DAO<T> и т.д.

Чисто Java-решение

В статье Доступ к универсальным типам во время выполнения в Java объясняется, как вы можете это сделать, используя чистую Java.

@SuppressWarnings("unchecked")
public Class reflectClassType() {
return ((Class) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0]);
}

У этого кода есть несколько ограничений. Он не будет работать во всех случаях, связанных с несколькими уровнями абстракции, или когда ваш тип представляет собой массив (например, int[]). Для более полного решения, которое расширяет это для большего количества случаев, смотрите эту статью .

Еще раз обратите внимание, что этот метод работает только с подклассом, не являющимся универсальным / raw. Если вы попытаетесь сделать это с универсальным классом, аргументы типа останутся неразрешенными, останутся универсальные TypeVariableImpl объекты, классом которых является null. Например:

class Generic<T>{
@SuppressWarnings("unchecked")
public Class reflectClassType() {
return ((Class) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0]);
}
}

class ChildGeneric<T> extends Generic<T>{}
class ChildString extends Generic<String>{}

// Error! Won't work, since the subclass is generic
new ChildGeneric<String>().reflectClassType();
// Works; we create an anonymous, trivial, non-generic subclass
new ChildGeneric<String>(){}.reflectClsasType();
// Works; we create an explicit non-generic subclass
new ChildString().reflectClassType(); // okay

Spring solution

Spring provides a utility class GenericTypeResolver, which implements a more robust version of this technique. It comes with its own subtle limitations. In general, it will only work if the called for a non-generic class.

My project was using Spring so 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

Note that in this code, the non-generic Foo subclass has a generic superclass AbstractFoo:

public class Foo extends AbstractFoo<GenericType>

which is what allows this method to work.

Ответ 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;
}
}
}

Тогда:

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 );
}
}
Ответ 4

Стандартный подход / обходной путь / решение заключается в добавлении class объекта в конструктор (ы), например:

 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();
}
}
java generics