Obter tipo genérico de classe em tempo de execução

508

Como posso conseguir isso?

public class GenericClass<T>
{
    public Type getMyType()
    {
        //How do I return the type of T?
    }
}

Tudo o que tentei até agora sempre retorna o tipo, Objecte não o tipo específico usado.

Glenn
fonte
3
@SamuelVidal Isso não é .NET, é Java.
Frontear 18/07/19

Respostas:

317

Como outros mencionaram, só é possível através da reflexão em determinadas circunstâncias.

Se você realmente precisa do tipo, este é o padrão comum de solução alternativa (com segurança de tipo):

public class GenericClass<T> {

     private final Class<T> type;

     public GenericClass(Class<T> type) {
          this.type = type;
     }

     public Class<T> getMyType() {
         return this.type;
     }
}
Henning
fonte
58
Eu gosto desta resposta, mas é um pouco complicado instanciar: GenericClass <AnotherClass> g = new GenericClass <AnotherClass> (AnotherClass.class);
Eliseo Ocampos
1
É ainda mais detalhado se você usar uma abordagem dao / factory / manager. Foo foo1 = GetDao<Foo>(Foo.class).get(Foo.class, 1)
djmj
2
Isso é verdade, mas não funciona em todos os casos, como beans remotos sem estado que são instanciados por contêiner / reflexão.
djmj
6
Como acompanhamento do meu comentário anterior - depois de muita dor brincando com a reflexão, acabei usando esta resposta.
Tomáš Zato - Restabelece Monica
18
Você pode contornar a referência supérflua, fornecendo um método genérico de fábrica estática. Algo como public static <T> GenericClass<T> of(Class<T> type) {...}e, em seguida, chamá-lo como tal: GenericClass<String> var = GenericClass.of(String.class). Um pouco melhor.
Joeri Hendrickx
263

Eu vi algo parecido com isto

private Class<T> persistentClass;

public Constructor() {
    this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
                            .getGenericSuperclass()).getActualTypeArguments()[0];
 }

no exemplo GenericDataAccessObjects hibernar

FrVaBe
fonte
84
Essa técnica funciona onde o parâmetro type é definido na superclasse imediata, mas falha se o parâmetro type for definido em outro lugar na hierarquia de tipos. Para lidar com casos mais complexos, algo como o TypeTools pode ser usado. Os documentos incluem um exemplo de um DAO genérico mais sofisticado.
19611 Jonathan
25
Isso retorna apenas os parâmetros de tipo reais usados ​​quando uma CLASSE implementa / estende algo que possui declarações genéricas; não retorna os parâmetros de tipo reais usados ​​quando uma INSTANCE é instanciada. Em outras palavras, PODE dizer que em class A implements Comparable<String>, o parâmetro de tipo real é String, mas NÃO PODE dizer que em Set<String> a = new TreeSet<String>(), o parâmetro de tipo real é String. De fato, as informações do parâmetro type são "apagadas" após a compilação, conforme explicado em outras respostas.
você precisa saber é o seguinte
32
Estou recebendo java.lang.Class cannot be cast to java.lang.reflect.ParameterizedTypeesta resposta.
Tomáš Zato - Restabelece Monica
4
Essa abordagem também pode ser alcançada usando Class-Mateo pessoal de Jackson. Eu escrevi uma essência aqui gist.github.com/yunspace/930d4d40a787a1f6a7d1
yunspace
5
@ TomášZato Chamar simplesmente o código acima retornou a mesma exceção para mim. Eu sei que é um pouco tarde, mas de qualquer maneira, no meu caso, eu tive que ligar (Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments()para obter argumentos de tipo reais.
AndrewMcCoist 13/02/2019
99

Os genéricos não são reificados em tempo de execução. Isso significa que as informações não estão presentes no tempo de execução.

Adicionar genéricos ao Java e manter a compatibilidade com versões anteriores foi um tour de force (você pode ver o documento seminal sobre o assunto: Tornando o futuro seguro para o passado: adicionando genéricos à linguagem de programação Java ).

Há uma rica literatura sobre o assunto, e algumas pessoas estão insatisfeitas com o estado atual, outros dizem que, na verdade, é uma atração e não há necessidade real disso. Você pode ler os dois links, eu os achei bastante interessantes.

ewernli
fonte
179
É claro que estamos insatisfeitos, NET tem muito melhor genérico mecanismo de manipulação
Pacerier
6
@ Pacerier: mas os genéricos reificados por si só não levariam o Java ao nível do .NET. O valor digita um código especializado para eles é pelo menos igualmente importante por que o .NET é melhor na área de genéricos.
Joachim Sauer
@JoachimSauer, yes tipos de valor. Eu sempre quis aqueles em java. Btw, o que você quer dizer com código especializado?
Juliet
3
@ spaaarky21 Não, os parâmetros do tipo genérico são removidos durante a compilação (o chamado "apagamento", você pode pesquisá-lo no Google). O truque na resposta de FrVaBe só funciona se os parâmetros tipo da superclasse são conhecidos estaticamente (ver a primeira commment por Johnathn)
ewernli
1
O apagamento do tipo Java é uma falha de design histórico; mais código foi escrito para contorná-lo do que foi escrito para implementá-lo.
Abhijit Sarkar
68

Use goiaba.

import com.google.common.reflect.TypeToken;
import java.lang.reflect.Type;

public abstract class GenericClass<T> {
  private final TypeToken<T> typeToken = new TypeToken<T>(getClass()) { };
  private final Type type = typeToken.getType(); // or getRawType() to return Class<? super T>

  public Type getType() {
    return type;
  }

  public static void main(String[] args) {
    GenericClass<String> example = new GenericClass<String>() { };
    System.out.println(example.getType()); // => class java.lang.String
  }
}

Há algum tempo, publiquei alguns exemplos completos, incluindo classes abstratas e subclasses aqui .

Nota: isso requer que você instancia uma subclasse de GenericClasspara que possa ligar o parâmetro type corretamente. Caso contrário, ele retornará o tipo como T.

Cody A. Ray
fonte
3
Observe que eu crio uma subclasse anônima vazia (veja as duas chaves no final). Isso usa reflexão para combater o apagamento do tipo de tempo de execução do Java. Você pode saber mais aqui: code.google.com/p/guava-libraries/wiki/ReflectionExplained
Cody A. Ray
3
@ CodyA.Ray Seu código lança a java.lang.IllegalArgumentException: class com.google.common.reflect.TypeToken isn't parameterized. Então eu mudei a linha new TypeToken(getClass()) { }para new TypeToken<T>(getClass()) { }. Agora, o código funciona bem, mas o Type ainda é 'T'. Veja isto: gist.github.com/m-manu/9cda9d8f9d53bead2035
Manu Manjunath
3
@ Dominik Por favor, veja o exemplo atualizado que você pode copiar e colar para testar a si mesmo. Também adicionei uma nota esclarecendo que você deve instanciar uma subclasse (como mostrado). Como aconselhamento geral sobre etiqueta, leia todos os artigos vinculados e javadocs relacionados antes de acusar um pôster de "pensamento positivo". Eu usei código de produção semelhante várias vezes. Os ajudantes do Guava que estou demonstrando destinam-se a esse caso de uso exato e seus javadocs mostram quase uma resposta exata para esta pergunta. docs.guava-libraries.googlecode.com/git/javadoc/com/google/…
Cody A. Ray
1
Essa resposta funciona muito bem com o Java8 - eu tenho uma interface e uma classe de fábrica para emitir vários tipos e queria que os usuários pudessem determinar facilmente o tipo. Na minha interface, adicionei o método padrão: default Type getParameterType() { final TypeToken<T> typeToken = new TypeToken<T>(getClass()) {}; final Type type = typeToken.getType(); return type; }
Richard Sand
2
@ CodyA.Ray Como isso funciona apenas com subclasses de GenericClass, você deve fazer essa classe abstractpara que o uso errado não seja compilado.
Martin
37

Certamente você pode.

Java não usa as informações no tempo de execução, por motivos de compatibilidade com versões anteriores. Mas as informações estão realmente presentes como metadados e podem ser acessadas via reflexão (mas ainda não são usadas para verificação de tipo).

A partir da API oficial:

http://download.oracle.com/javase/6/docs/api/java/lang/reflect/ParameterizedType.html#getActualTypeArguments%28%29

No entanto , para o seu cenário, eu não usaria a reflexão. Pessoalmente, estou mais inclinado a usar isso como código de estrutura. No seu caso, eu apenas adicionaria o tipo como um parâmetro do construtor.

Joeri Hendrickx
fonte
10
getActualTypeArguments retorna apenas os argumentos de tipo para a classe imediata. Se você tiver uma hierarquia de tipos complexa em que T possa ser parametrizado em qualquer lugar da hierarquia, precisará trabalhar um pouco para descobrir o que é. Isso é mais ou menos o que o TypeTools faz.
19611 Jonathan
36

Os genéricos Java geralmente são em tempo de compilação, isso significa que as informações do tipo são perdidas no tempo de execução.

class GenericCls<T>
{
    T t;
}

será compilado para algo como

class GenericCls
{
   Object o;
}

Para obter as informações de tipo em tempo de execução, você deve adicioná-las como argumento do ctor.

class GenericCls<T>
{
     private Class<T> type;
     public GenericCls(Class<T> cls)
     {
        type= cls;
     }
     Class<T> getType(){return type;}
}

Exemplo:

GenericCls<?> instance = new GenericCls<String>(String.class);
assert instance.getType() == String.class;
josefx
fonte
9
private final Class<T> type;
NAXA
Como posso criar um tipo de matriz a partir dele:Type t = //String[]
Pawel Cioch
@PawelCioch java.lang.reflect.Array.newInstance (elementtype, length); espero que isso ajude (javadoc pode ser encontrada aqui docs.oracle.com/javase/8/docs/api/java/lang/reflect/... )
josefx
O @PawelCioch perdeu um .getClass () para obter o tipo da matriz criada. Não parece haver uma maneira direta de obter uma classe de matriz. A maioria das coleções Java apenas usa Object [].
josefx
23
public abstract class AbstractDao<T>
{
    private final Class<T> persistentClass;

    public AbstractDao()
    {
        this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments()[0];
    }
}
Ali Yeganeh
fonte
3
Estou votando positivamente nesta resposta porque é uma solução que funciona para a pergunta que está sendo feita. No entanto, para quem deseja navegar para cima na hierarquia de classes como eu com mais de uma classe genérica, isso não funcionará. Porque você obterá java.lang.object em vez da classe real.
KMC22
3
Por favor, note que esta solução só funciona se a classe que contém o tipo genérico é RESUMO
BabaNew
Não funcionou com a minha classe abstrata em Java 11
JRA_TLL
@JRA_TLL você aparentemente fez algo errado. Eu apenas o usei com o Java 12 e funciona como um encanto.
Googie
Se você deseja navegar na hierarquia de exibição, pode converter a Superclasse genérica na Classe <*> e obter a Superclasse genérica. De preferência em um loop.
fupduck 2/06
21

Eu usei a seguinte abordagem:

public class A<T> {

    protected Class<T> clazz;

    public A() {
        this.clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public Class<T> getClazz() {
        return clazz;
    }
}

public class B extends A<C> {
   /* ... */
    public void anything() {
       // here I may use getClazz();
    }
}
Moesio
fonte
9
Eu tenho "Exception in thread 'main' java.lang.ClassCastException: java.lang.Class não pode ser convertido para java.lang.reflect.ParameterizedType" com este código de exemplo
yuyang
16

Eu não acho que você pode, o Java usa apagamento de tipo ao compilar para que seu código seja compatível com aplicativos e bibliotecas que foram criadas pré-genéricas.

No Oracle Docs:

Tipo Erasure

Os genéricos foram introduzidos na linguagem Java para fornecer verificações mais restritas de tipo em tempo de compilação e para oferecer suporte à programação genérica. Para implementar genéricos, o compilador Java aplica o apagamento de tipo a:

Substitua todos os parâmetros de tipo em tipos genéricos por seus limites ou Objeto se os parâmetros de tipo não tiverem limites. O bytecode produzido, portanto, contém apenas classes, interfaces e métodos comuns. Insira moldes do tipo, se necessário, para preservar a segurança do tipo. Gere métodos de ponte para preservar o polimorfismo em tipos genéricos estendidos. O apagamento de tipo garante que nenhuma nova classe seja criada para tipos parametrizados; consequentemente, os genéricos não sofrem sobrecarga de tempo de execução.

http://docs.oracle.com/javase/tutorial/java/generics/erasure.html

Dimitar
fonte
Jup, é impossível. Java precisaria de genéricos reificados para que isso funcionasse.
Henning
15

Técnica descrita neste artigo por Ian Robertson funciona para mim.

Em resumo, exemplo rápido e sujo:

 public abstract class AbstractDAO<T extends EntityInterface, U extends QueryCriteria, V>
 {
    /**
     * Method returns class implementing EntityInterface which was used in class
     * extending AbstractDAO
     *
     * @return Class<T extends EntityInterface>
     */
    public Class<T> returnedClass()
    {
        return (Class<T>) getTypeArguments(AbstractDAO.class, getClass()).get(0);
    }

    /**
     * Get the underlying class for a type, or null if the type is a variable
     * type.
     *
     * @param type the type
     * @return the underlying class
     */
    public static Class<?> getClass(Type type)
    {
        if (type instanceof Class) {
            return (Class) type;
        } else if (type instanceof ParameterizedType) {
            return getClass(((ParameterizedType) type).getRawType());
        } else if (type instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            Class<?> componentClass = getClass(componentType);
            if (componentClass != null) {
                return Array.newInstance(componentClass, 0).getClass();
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * Get the actual type arguments a child class has used to extend a generic
     * base class.
     *
     * @param baseClass the base class
     * @param childClass the child class
     * @return a list of the raw classes for the actual type arguments.
     */
    public static <T> List<Class<?>> getTypeArguments(
            Class<T> baseClass, Class<? extends T> childClass)
    {
        Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
        Type type = childClass;
        // start walking up the inheritance hierarchy until we hit baseClass
        while (!getClass(type).equals(baseClass)) {
            if (type instanceof Class) {
                // there is no useful information for us in raw types, so just keep going.
                type = ((Class) type).getGenericSuperclass();
            } else {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                Class<?> rawType = (Class) parameterizedType.getRawType();

                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
                for (int i = 0; i < actualTypeArguments.length; i++) {
                    resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
                }

                if (!rawType.equals(baseClass)) {
                    type = rawType.getGenericSuperclass();
                }
            }
        }

        // finally, for each actual type argument provided to baseClass, determine (if possible)
        // the raw class for that type argument.
        Type[] actualTypeArguments;
        if (type instanceof Class) {
            actualTypeArguments = ((Class) type).getTypeParameters();
        } else {
            actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
        }
        List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
        // resolve types by chasing down type variables.
        for (Type baseType : actualTypeArguments) {
            while (resolvedTypes.containsKey(baseType)) {
                baseType = resolvedTypes.get(baseType);
            }
            typeArgumentsAsClasses.add(getClass(baseType));
        }
        return typeArgumentsAsClasses;
    }
  }
Ondrej Bozek
fonte
3
Em que linha específica desse código são reais, os parâmetros do tipo em tempo de execução sendo lidos?
precisa saber é o seguinte
aqui? Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Ondrej Bozek
9

Eu acho que há outra solução elegante.

O que você deseja fazer é (com segurança) "passar" o tipo do parâmetro de tipo genérico da classe concerete para a superclasse.

Se você se permitir pensar no tipo de classe como "metadados" na classe, isso sugere o método Java para codificar metadados no tempo de execução: anotações.

Primeiro defina uma anotação personalizada ao longo destas linhas:

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityAnnotation {
    Class entityClass();
}

Você pode adicionar a anotação à sua subclasse.

@EntityAnnotation(entityClass =  PassedGenericType.class)
public class Subclass<PassedGenericType> {...}

Então você pode usar este código para obter o tipo de classe na sua classe base:

import org.springframework.core.annotation.AnnotationUtils;
.
.
.

private Class getGenericParameterType() {
    final Class aClass = this.getClass();
    EntityAnnotation ne = 
         AnnotationUtils.findAnnotation(aClass, EntityAnnotation.class);

    return ne.entityClass();
}

Algumas limitações dessa abordagem são:

  1. Você especifica o tipo genérico ( PassedGenericType) em DOIS lugares, em vez de um que não seja SECO.
  2. Isso só é possível se você puder modificar as subclasses de concreto.
DaBlick
fonte
Sim, não é SECO, mas é mais limpo que a abordagem de extensão sugerida acima. Eu gostei. obrigado
agodinhost 11/01/19
8

Esta é a minha solução:

import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;

public class GenericClass<T extends String> {

  public static void main(String[] args) {
     for (TypeVariable typeParam : GenericClass.class.getTypeParameters()) {
      System.out.println(typeParam.getName());
      for (Type bound : typeParam.getBounds()) {
         System.out.println(bound);
      }
    }
  }
}
Matthias M
fonte
10
Esta não é uma resposta para esta pergunta.
Adam Arold
1
Meu código não é a solução exata para a pergunta. Ele retorna os parâmetros de tipo genérico da classe, mas não o tipo real de T. Mas pode ser útil para outras pessoas que se deparam com a pergunta e estão procurando minha solução.
Matthias M
1
getClass (). getGenericSuperclass () obterá o mesmo efeito.
Gorky
5

Aqui está a 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 <> ");
        }
    } 

OBSERVAÇÕES: Pode ser usado apenas como superclasse
1. Deve ser estendido com a classe digitada ( Child extends Generic<Integer>)
OU

2. Deve ser criado como implementação anônima ( new Generic<Integer>() {};)

Yaroslav Kovbas
fonte
4

Você não pode. Se você adicionar uma variável de membro do tipo T à classe (você nem precisa inicializá-la), poderá usá-la para recuperar o tipo.

andrewmu
fonte
2
Opa, ok. Você não tem que inicialize-o a partir de um construtor.
precisa saber é o seguinte
4

Aqui está uma maneira, que eu tive que usar uma ou duas vezes:

public abstract class GenericClass<T>{
    public abstract Class<T> getMyType();
}

Junto com

public class SpecificClass extends GenericClass<String>{

    @Override
    public Class<String> getMyType(){
        return String.class;
    }
}
PJWeisberg
fonte
4
Isso tecnicamente funciona, mas não resolve o caso geral, e acho que é isso que o pôster original procura.
precisa saber é o seguinte
Isso não merece ser rejeitado como está - o pôster original não foi explícito. Esta resposta oferece um padrão de design que faz o trabalho e é fácil de implementar, desde que seja adequado para fazer a classe genérica abstrato.
VirtualMichael
4

Uma solução simples para esta cabine é como abaixo

public class GenericDemo<T>{
    private T type;

    GenericDemo(T t)
    {
        this.type = t;
    }

    public String getType()
    {
        return this.type.getClass().getName();
    }

    public static void main(String[] args)
    {
        GenericDemo<Integer> obj = new  GenericDemo<Integer>(5);
        System.out.println("Type: "+ obj.getType());
    }
}
Paul92
fonte
3

Para concluir algumas das respostas aqui, tive que obter o ParametrizedType de MyGenericClass, não importa quão alta seja a hierarquia, com a ajuda da recursão:

private Class<T> getGenericTypeClass() {
        return (Class<T>) (getParametrizedType(getClass())).getActualTypeArguments()[0];
}

private static ParameterizedType getParametrizedType(Class clazz){
    if(clazz.getSuperclass().equals(MyGenericClass.class)){ // check that we are at the top of the hierarchy
        return (ParameterizedType) clazz.getGenericSuperclass();
    } else {
        return getParametrizedType(clazz.getSuperclass());
    }
}
galex
fonte
1

Aqui está o meu truque:

public class Main {

    public static void main(String[] args) throws Exception {

        System.out.println(Main.<String> getClazz());

    }

    static <T> Class getClazz(T... param) {

        return param.getClass().getComponentType();
    }

}
HRgiger
fonte
1
Nota: isso não funciona quando Té uma variável de tipo. No caso de Tuma variável de tipo, as varargs criam uma matriz de apagamento de T. Veja, por exemplo, http://ideone.com/DIPNwd .
Radiodef 11/04/2015
1
Isso retorna "Object"
yahya
Pode ser que você esteja tentando responder a outra pergunta
Khan
1

No caso de você usar armazenar uma variável usando o tipo genérico, você pode resolver facilmente esse problema adicionando um método getClassType da seguinte maneira:

public class Constant<T> {
  private T value;

  @SuppressWarnings("unchecked")
  public Class<T> getClassType () {
    return ((Class<T>) value.getClass());
  }
}

Uso o objeto de classe fornecido posteriormente para verificar se é uma instância de uma determinada classe, da seguinte maneira:

Constant<?> constant = ...;
if (constant.getClassType().equals(Integer.class)) {
    Constant<Integer> integerConstant = (Constant<Integer>)constant;
    Integer value = integerConstant.getValue();
    // ...
}
Pablo Trinidad
fonte
2
Isso é problemático, infelizmente. Primeiro de tudo, e se valuefor null? Em segundo lugar, e se valuefor uma subclasse de T? Constant<Number> c = new Constant<Number>(new Integer(0)); Class<Number> n = c.getClassType();retorna Integer.classquando deveria retornar Number.class. Seria mais correto retornar Class<? extends T>. Integer.class é um Class<? extends Number> mas não um Class<Number>.
Radiodef
1

Aqui está a minha solução

public class GenericClass<T>
{
    private Class<T> realType;

    public GenericClass() {
        findTypeArguments(getClass());
    }

    private void findTypeArguments(Type t) {
        if (t instanceof ParameterizedType) {
            Type[] typeArgs = ((ParameterizedType) t).getActualTypeArguments();
            realType = (Class<T>) typeArgs[0];
        } else {
            Class c = (Class) t;
            findTypeArguments(c.getGenericSuperclass());
        }
    }

    public Type getMyType()
    {
        // How do I return the type of T? (your question)
        return realType;
    }
}

Não importa quantos níveis sua hierarquia de classes tenha, esta solução ainda funciona, por exemplo:

public class FirstLevelChild<T> extends GenericClass<T> {

}

public class SecondLevelChild extends FirstLevelChild<String> {

}

Nesse caso, getMyType () = java.lang.String

Lin Yu Cheng
fonte
Isso não está retornando o tipo de T. Ele está retornando T não java.lang.String além do código não está a converter Type para Class <T>
Mohy Eldeen
Aqui está uma amostra online que eu fiz. Clique em compilar e executar, para obter o resultado. tutorialspoint.com/…
Lin Yu Cheng
Funciona para mim - quando o WildFly Weld CDI quebrou um método alternativo.
26717 Andre Andre
Eu conseguiException in thread "main" java.lang.NullPointerException at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.<init>(Main.java:43) at Main.main(Main.java:61)
yuyang 20/03/19
0
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
    return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}
kayz1
fonte
0

Aqui está a minha solução. Os exemplos devem explicar isso. O único requisito é que uma subclasse deve definir o tipo genérico, não um objeto.

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;

public class TypeUtils {

    /*** EXAMPLES ***/

    public static class Class1<A, B, C> {

        public A someA;
        public B someB;
        public C someC;

        public Class<?> getAType() {
            return getTypeParameterType(this.getClass(), Class1.class, 0);
        }

        public Class<?> getCType() {
            return getTypeParameterType(this.getClass(), Class1.class, 2);
        }
    }

    public static class Class2<D, A, B, E, C> extends Class1<A, B, C> {

        public B someB;
        public D someD;
        public E someE;
    }

    public static class Class3<E, C> extends Class2<String, Integer, Double, E, C> {

        public E someE;
    }

    public static class Class4 extends Class3<Boolean, Long> {

    }

    public static void test() throws NoSuchFieldException {

        Class4 class4 = new Class4();
        Class<?> typeA = class4.getAType(); // typeA = Integer
        Class<?> typeC = class4.getCType(); // typeC = Long

        Field fieldSomeA = class4.getClass().getField("someA");
        Class<?> typeSomeA = TypeUtils.getFieldType(class4.getClass(), fieldSomeA); // typeSomeA = Integer

        Field fieldSomeE = class4.getClass().getField("someE");
        Class<?> typeSomeE = TypeUtils.getFieldType(class4.getClass(), fieldSomeE); // typeSomeE = Boolean


    }

    /*** UTILS ***/

    public static Class<?> getTypeVariableType(Class<?> subClass, TypeVariable<?> typeVariable) {
        Map<TypeVariable<?>, Type> subMap = new HashMap<>();
        Class<?> superClass;
        while ((superClass = subClass.getSuperclass()) != null) {

            Map<TypeVariable<?>, Type> superMap = new HashMap<>();
            Type superGeneric = subClass.getGenericSuperclass();
            if (superGeneric instanceof ParameterizedType) {

                TypeVariable<?>[] typeParams = superClass.getTypeParameters();
                Type[] actualTypeArgs = ((ParameterizedType) superGeneric).getActualTypeArguments();

                for (int i = 0; i < typeParams.length; i++) {
                    Type actualType = actualTypeArgs[i];
                    if (actualType instanceof TypeVariable) {
                        actualType = subMap.get(actualType);
                    }
                    if (typeVariable == typeParams[i]) return (Class<?>) actualType;
                    superMap.put(typeParams[i], actualType);
                }
            }
            subClass = superClass;
            subMap = superMap;
        }
        return null;
    }

    public static Class<?> getTypeParameterType(Class<?> subClass, Class<?> superClass, int typeParameterIndex) {
        return TypeUtils.getTypeVariableType(subClass, superClass.getTypeParameters()[typeParameterIndex]);
    }

    public static Class<?> getFieldType(Class<?> clazz, AccessibleObject element) {
        Class<?> type = null;
        Type genericType = null;

        if (element instanceof Field) {
            type = ((Field) element).getType();
            genericType = ((Field) element).getGenericType();
        } else if (element instanceof Method) {
            type = ((Method) element).getReturnType();
            genericType = ((Method) element).getGenericReturnType();
        }

        if (genericType instanceof TypeVariable) {
            Class<?> typeVariableType = TypeUtils.getTypeVariableType(clazz, (TypeVariable) genericType);
            if (typeVariableType != null) {
                type = typeVariableType;
            }
        }

        return type;
    }

}
PetermaxX
fonte
-1

Fiz o mesmo que o @Moesio Above, mas em Kotlin isso poderia ser feito da seguinte maneira:

class A<T : SomeClass>() {

    var someClassType : T

    init(){
    this.someClassType = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<T>
    }

}
Bonestack
fonte
-4

Pode ser útil para alguém. Você pode usar java.lang.ref.WeakReference; por aqui:

class SomeClass<N>{
  WeakReference<N> variableToGetTypeFrom;

  N getType(){
    return variableToGetTypeFrom.get();
  }
}
TheLetch
fonte
Como essa classe deve ser usada? Por que WeakReference? Forneça alguma explicação com sua resposta, não apenas algum código.
Abhijit Sarkar
Portanto, se você tiver um, SomeClass<MyClass> pode instanciar SomeClasse chamar getTypenessa instância e ter o tempo de execução atual MyClass.
TheLetch
Claro, mas por quê WeakReference? O que você disse não é diferente da maioria das outras respostas.
Abhijit Sarkar
Primeiro a minha abordagem é mais curto (menos código), segundo as referências fracas não impedem seus referentes de ser feita finalizable, e, tanto quanto eu sei que não usa reflexão, portanto, é rápido
TheLetch
Isso não obter o tipo de coisa, isso retorna um objeto desse tipo, que, fyi, você pode fazer com literalmente qualquer tipo de invólucro ( AtomicReference, List, Set).
Frontear
-5

Achei que era uma solução simples, compreensível e facilmente explicável

public class GenericClass<T> {

    private Class classForT(T...t) {
        return t.getClass().getComponentType();
    }

    public static void main(String[] args) {
        GenericClass<String> g = new GenericClass<String>();

        System.out.println(g.classForT());
        System.out.println(String.class);
    }
}
Mark Karchner
fonte
Explique (T...t). (É por isso que esse código não funciona).
Christopher Schneider