Como obter uma instância de classe de genéricos Tipo T

Tenho uma aula de genéricos. Em um método de Foo, Eu quero obter a instância de classe do tipo T, mas eu simplesmente não posso ligar T.class.

Qual é a maneira preferida de contornar isso usando T.class?

 533
Author: Peter Mortensen, 2010-08-09

17 answers

A resposta curta é, que não há maneira de descobrir o tipo de tempo de execução de Parâmetros de tipo genérico em Java. Sugiro ler o capítulo sobre o apagamento do tipo no Tutorial Java para mais detalhes.

Uma solução popular para isto é passar o Class do parâmetro do tipo para o construtor do tipo genérico, por exemplo

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
    }
}
 454
Author: Zsolt Török, 2010-08-09 07:09:48
Estava à procura de uma forma de fazer isto sozinho, sem adicionar uma dependência extra ao classpath. Depois de uma investigação, descobri que é possível, desde que você tenha um supertype Genérico. Isso foi OK para mim, Pois eu estava trabalhando com uma camada DAO com um supertipo de camada genérica. Se isto se encaixa no teu cenário, então é a melhor abordagem IMHO.

A maioria dos genéricos usa casos que encontrei têm algum tipo de supertipo Genérico, por exemplo List<T> Para ArrayList<T> ou GenericDAO<T> para DAO<T>, etc.

Solução Java pura

O artigo a aceder aos tipos genéricos ao executar em Java explica como você pode fazer isso usando Java puro.

Solução de Primavera

O meu projecto estava a usarA Primavera o que é ainda melhor porque a primavera tem um método de utilidade útil para encontrar o tipo. Esta é a melhor abordagem para mim como ele parece melhor. Acho que se não estivesses a usar a Primavera podias escrever o teu próprio método de utilidade.

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 ";
    }
 182
Author: Ben Thurley, 2017-10-02 11:17:15
No entanto, há uma pequena lacuna: se definirmos a nossa classe como abstracta. Isso significa que você tem que instanciar sua classe como: [[12]}
Foo<MyType> myFoo = new Foo<MyType>(){};

(Note o duplo aparelho no final.)

Agora você pode recuperar o tipo de T em tempo de execução:

Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];

Note no entanto que mySuperclass tem de ser a superclasse da definição da classe que define realmente o tipo final para T.

Também não é muito elegante, mas você tem que decidir se prefere {[[8]} ou No seu código.


Por exemplo:

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;
    }
  }
}

Depois:

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 );
    }
}
 79
Author: bert bruynooghe, 2017-08-07 11:37:59

Uma abordagem-padrão / solução é adicionar um objecto class ao(S) construtor (s), como:

 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();
    }
 }
 33
Author: Andreas_D, 2012-02-09 17:37:20

Imagine que você tem uma superclasse abstrata que é genérica:

public abstract class Foo<? extends T> {}

E então você tem uma segunda classe que se estende Foo com uma barra genérica que se estende T:

public class Second extends Foo<Bar> {}

Você pode obter a classe Bar.class na classe Foo seleccionando a Type (a partir da resposta de bert bruynooghe) e inferindo-a usando Class instância:

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);

Você tem que notar que esta operação não é ideal, então é uma boa idéia cache o valor calculado para evitar vários cálculos sobre isso. Um dos típicos os usos estão na implementação genérica DAO.

A execução final:

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;
    }
}

O valor devolvido é a barra.classe quando invocada a partir da classe Foo noutra função ou da classe de barras.

 17
Author: droidpl, 2014-12-24 10:32:07

Aqui está uma solução de trabalho:

@SuppressWarnings("unchecked")
private Class<T> getGenericTypeClass() {
    try {
        String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();
        Class<?> clazz = Class.forName(className);
        return (Class<T>) clazz;
    } catch (Exception e) {
        throw new IllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");
    }
} 

Notas: só pode ser usado como superclasse

  1. tem de ser alargado com classe tipada(Child extends Generic<Integer>)

OU

  1. Tem de ser criado como implementação anónima.(new Generic<Integer>() {};)
 10
Author: Yaroslav Kovbas, 2018-06-17 08:55:59
Tive este problema numa aula genérica abstracta. Neste caso específico, a solução é mais simples:
abstract class Foo<T> {
    abstract Class<T> getTClass();
    //...
}

E mais tarde na classe derivada:

class Bar extends Foo<Whatever> {
    @Override
    Class<T> getTClass() {
        return Whatever.class;
    }
}
 8
Author: user1154664, 2014-06-05 23:36:38
Não podes fazê-lo por causa do tipo de apagamento. Veja também a questão do excesso de pilhaJava generics - tipo de apagamento-quando e o que acontece.
 8
Author: Konrad Garus, 2017-05-23 12:18:30

Uma rota melhor do que a classe que os outros sugeriram é passar por um objeto que pode fazer o que você teria feito com a classe, por exemplo, criar uma nova instância.

interface Factory<T> {
  T apply();
}

<T> void List<T> make10(Factory<T> factory) {
  List<T> result = new ArrayList<T>();
  for (int a = 0; a < 10; a++)
    result.add(factory.apply());
  return result;
}

class FooFactory<T> implements Factory<Foo<T>> {
  public Foo<T> apply() {
    return new Foo<T>();
  }
}

List<Foo<Integer>> foos = make10(new FooFactory<Integer>());
 7
Author: Ricky Clarkson, 2013-03-14 13:04:00
É possível.
class Foo<T> {
  Class<T> clazz = (Class<T>) DAOUtil.getTypeArguments(Foo.class, this.getClass()).get(0);
}

Necessita de duas funções de svn/trunk/dao/src/main/java/com/googlecode/genericdao/dao/ DAOUtil.java .

Para mais explicações, ver genéricos reflectores.

 4
Author: Peter Tseng, 2015-09-27 08:33:03

Tenho uma solução (feia mas eficaz) para este problema, que usei recentemente:

import java.lang.reflect.TypeVariable;


public static <T> Class<T> getGenericClass()
{
    __<T> ins = new __<T>();
    TypeVariable<?>[] cls = ins.getClass().getTypeParameters(); 

    return (Class<T>)cls[0].getClass();
}

private final class __<T> // generic helper class which does only provide type information
{
    private __()
    {
    }
}
 4
Author: Unknown6656, 2017-11-07 06:20:34
Encontrei uma maneira genérica e simples de fazer isso. Na minha classe, criei um método que devolve o tipo genérico de acordo com a sua posição na definição de classe. Vamos assumir uma definição de classe como esta:
public class MyClass<A, B, C> {

}
Agora vamos criar alguns atributos para persistir os tipos:
public class MyClass<A, B, C> {

    private Class<A> aType;

    private Class<B> bType;

    private Class<C> cType;

// Getters and setters (not necessary if you are going to use them internally)

    } 

Então poderá criar um método genérico que devolve o tipo com base no índice da definição genérica:

   /**
     * Returns a {@link Type} object to identify generic types
     * @return type
     */
    private Type getGenericClassType(int index) {
        // To make it use generics without supplying the class type
        Type type = getClass().getGenericSuperclass();

        while (!(type instanceof ParameterizedType)) {
            if (type instanceof ParameterizedType) {
                type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
            } else {
                type = ((Class<?>) type).getGenericSuperclass();
            }
        }

        return ((ParameterizedType) type).getActualTypeArguments()[index];
    }
Finalmente, no construtor basta ligar para o método e enviar o índice para tipo. O código completo deve ser parecido com:
public class MyClass<A, B, C> {

    private Class<A> aType;

    private Class<B> bType;

    private Class<C> cType;


    public MyClass() {
      this.aType = (Class<A>) getGenericClassType(0);
      this.bType = (Class<B>) getGenericClassType(1);
      this.cType = (Class<C>) getGenericClassType(2);
    }

   /**
     * Returns a {@link Type} object to identify generic types
     * @return type
     */
    private Type getGenericClassType(int index) {

        Type type = getClass().getGenericSuperclass();

        while (!(type instanceof ParameterizedType)) {
            if (type instanceof ParameterizedType) {
                type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
            } else {
                type = ((Class<?>) type).getGenericSuperclass();
            }
        }

        return ((ParameterizedType) type).getActualTypeArguments()[index];
    }
}
 3
Author: Juan Urrego, 2018-01-03 14:21:01

Como explicado em outras respostas, para usar esta abordagem {[[2]}, você precisa estender a classe, mas isso parece um trabalho extra para fazer toda uma nova classe que a estende...

Então, tornando a classe abstrata, obriga-nos a estendê-la, satisfazendo assim a exigência de subclassificação. (usando o @Getter de lombok).
@Getter
public abstract class ConfigurationDefinition<T> {

    private Class<T> type;
    ...

    public ConfigurationDefinition(...) {
        this.type = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        ...
    }
}
Agora estendê-la sem definir uma nova classe. (Anote o {} no final... extended, but don't overwrite anything-unless you want to).
private ConfigurationDefinition<String> myConfigA = new ConfigurationDefinition<String>(...){};
private ConfigurationDefinition<File> myConfigB = new ConfigurationDefinition<File>(...){};
...
Class stringType = myConfigA.getType();
Class fileType = myConfigB.getType();
 2
Author: Riaan Schutte, 2017-09-20 03:01:24
   public <T> T yourMethodSignature(Class<T> type) {

        // get some object and check the type match the given type
        Object result = ...            

        if (type.isAssignableFrom(result.getClass())) {
            return (T)result;
        } else {
            // handle the error
        }
   }
 1
Author: Abel ANEIROS, 2014-04-02 12:45:55

Se estiver a extender ou a implementar qualquer classe/interface que esteja a usar genéricos , poderá obter o tipo genérico de classe/interface pai, sem modificar nenhuma classe / interface existente.

Pode haver três possibilidades.

Caso 1 Quando a sua classe está a estender uma classe que está a usar genéricos

public class TestGenerics {
    public static void main(String[] args) {
        Type type = TestMySuperGenericType.class.getGenericSuperclass();
        Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
        for(Type gType : gTypes){
            System.out.println("Generic type:"+gType.toString());
        }
    }
}

class GenericClass<T> {
    public void print(T obj){};
}

class TestMySuperGenericType extends GenericClass<Integer> {
}

Caso 2 Quando a sua classe está a implementar uma interface que está a usar genéricos

public class TestGenerics {
    public static void main(String[] args) {
        Type[] interfaces = TestMySuperGenericType.class.getGenericInterfaces();
        for(Type type : interfaces){
            Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
            for(Type gType : gTypes){
                System.out.println("Generic type:"+gType.toString());
            }
        }
    }
}

interface GenericClass<T> {
    public void print(T obj);
}

class TestMySuperGenericType implements GenericClass<Integer> {
    public void print(Integer obj){}
}

Caso 3 Quando interface está estendendo uma interface que está usando Generics

public class TestGenerics {
    public static void main(String[] args) {
        Type[] interfaces = TestMySuperGenericType.class.getGenericInterfaces();
        for(Type type : interfaces){
            Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
            for(Type gType : gTypes){
                System.out.println("Generic type:"+gType.toString());
            }
        }
    }
}

interface GenericClass<T> {
    public void print(T obj);
}

interface TestMySuperGenericType extends GenericClass<Integer> {
}
 1
Author: Anil Agrawal, 2016-12-05 10:43:57
Na verdade, acho que tens um campo na tua classe de tipo T. Se não há campo do Tipo T, Qual é o objectivo de ter um tipo genérico? Então, você pode simplesmente fazer uma demonstração nesse campo.

No meu caso, tenho um

Na minha turma, e eu verifico se o tipo de classe é "localidade" por
if (items.get(0) instanceof Locality) ...

Claro, isto só funciona se o número total de classes possíveis for limitado.

 0
Author: Christine, 2011-11-11 11:51:32
Estou a usar o método de trabalho para isto.
class MyClass extends Foo<T> {
....
}

MyClass myClassInstance = MyClass.class.newInstance();
 -4
Author: nikolai.serdiuk, 2017-06-27 08:45:27