serve - tipos genéricos java




Obter tipo de um parâmetro genérico em Java com reflexão (11)

A resposta rápida a pergunta é não, você não pode, por causa do apagamento de tipo genérico de Java.

A resposta mais longa seria que, se você criou sua lista assim:

new ArrayList<SpideMan>(){}

Então, neste caso, o tipo genérico é preservado na superclasse genérica da nova classe anônima acima.

Não que eu recomendo fazer isso com listas, mas é uma implementação de ouvinte:

new Listener<Type>() { public void doSomething(Type t){...}}

E como a extrapolação dos tipos genéricos de superclasses e superinterfaces muda entre as JVMs, a solução genérica não é tão direta quanto algumas respostas podem sugerir.

Aqui está agora eu fiz isso.

É possível obter o tipo de um parâmetro genérico?

Um exemplo:

public final class Voodoo {
    public static void chill(List<?> aListWithTypeSpiderMan) {
        // Here I'd like to get the Class-Object 'SpiderMan'
        Class typeOfTheList = ???;
    }

    public static void main(String... args) {
        chill(new ArrayList<SpiderMan>());
    }
}

Apêndice à resposta de @ DerMike para obter o parâmetro genérico de uma interface parametrizada (usando o método #getGenericInterfaces () dentro de um método padrão Java-8 para evitar duplicação):

import java.lang.reflect.ParameterizedType; 

public class ParametrizedStuff {

@SuppressWarnings("unchecked")
interface Awesomable<T> {
    default Class<T> parameterizedType() {
        return (Class<T>) ((ParameterizedType)
        this.getClass().getGenericInterfaces()[0])
            .getActualTypeArguments()[0];
    }
}

static class Beer {};
static class EstrellaGalicia implements Awesomable<Beer> {};

public static void main(String[] args) {
    System.out.println("Type is: " + new EstrellaGalicia().parameterizedType());
    // --> Type is: ParameterizedStuff$Beer
}

Eu codifiquei isso para métodos que esperam aceitar ou retornar Iterable<?...> . Aqui está o código:

/**
 * Assuming the given method returns or takes an Iterable<T>, this determines the type T.
 * T may or may not extend WindupVertexFrame.
 */
private static Class typeOfIterable(Method method, boolean setter)
{
    Type type;
    if (setter) {
        Type[] types = method.getGenericParameterTypes();
        // The first parameter to the method expected to be Iterable<...> .
        if (types.length == 0)
            throw new IllegalArgumentException("Given method has 0 params: " + method);
        type = types[0];
    }
    else {
        type = method.getGenericReturnType();
    }

    // Now get the parametrized type of the generic.
    if (!(type instanceof ParameterizedType))
        throw new IllegalArgumentException("Given method's 1st param type is not parametrized generic: " + method);
    ParameterizedType pType = (ParameterizedType) type;
    final Type[] actualArgs = pType.getActualTypeArguments();
    if (actualArgs.length == 0)
        throw new IllegalArgumentException("Given method's 1st param type is not parametrized generic: " + method);

    Type t = actualArgs[0];
    if (t instanceof Class)
        return (Class<?>) t;

    if (t instanceof TypeVariable){
        TypeVariable tv =  (TypeVariable) actualArgs[0];
        AnnotatedType[] annotatedBounds = tv.getAnnotatedBounds();///
        GenericDeclaration genericDeclaration = tv.getGenericDeclaration();///
        return (Class) tv.getAnnotatedBounds()[0].getType();
    }

    throw new IllegalArgumentException("Unknown kind of type: " + t.getTypeName());
}

Eu quero tentar quebrar a resposta de @DerMike para explicar:

Primeiro, o tipo de eliminação não significa que o JDK elimine as informações de tipo em tempo de execução. É um método para permitir que a verificação de tipo de tempo de compilação e a compatibilidade de tipo de tempo de execução coexistam no mesmo idioma. Como esse bloco de código implica, o JDK retém as informações do tipo apagadas - elas não são associadas a conversões verificadas e outras coisas.

Segundo, isso fornece informações de tipo genérico para uma classe genérica exatamente um nível acima da hierarquia do tipo concreto sendo verificado - ou seja, uma classe pai abstrata com parâmetros de tipo genérico pode encontrar os tipos concretos correspondentes a seus parâmetros de tipo para uma implementação concreta de si mesmo que herda diretamente dele. Se esta classe não fosse abstrata e instanciada, ou a implementação concreta fosse dois níveis abaixo, isso não funcionaria (embora um pouco de jimmy pudesse fazer com que ela se aplicasse a qualquer número predeterminado de níveis além de um, ou até a classe mais baixa com X parâmetros do tipo genérico, etc.

Enfim, para a explicação. Aqui está o código novamente, separado em linhas para facilitar a referência:

1# Class genericParameter0OfThisClass = 
2#     (Class)
3#         ((ParameterizedType)
4#             getClass()
5#                .getGenericSuperclass())
6#                    .getActualTypeArguments()[0];

Deixe-nos 'ser' a classe abstrata com tipos genéricos que contém este código. Lendo isto mais ou menos do avesso:

  • A linha 4 obtém a classe concreta atual 'Class instance. Isso identifica o tipo concreto do nosso descendente imediato.
  • A linha 5 obtém o supertipo dessa classe como um tipo; estes somos nós. Como somos um tipo paramétrico, podemos nos posicionar com segurança em ParameterizedType (linha 3). A chave é que, quando o Java determina esse objeto Type, ele usa as informações de tipo presentes no filho para associar informações de tipo aos nossos parâmetros de tipo na nova instância ParameterizedType. Então agora podemos acessar tipos de concreto para nossos genéricos.
  • A linha 6 obtém a matriz de tipos mapeados em nossos genéricos, conforme declarado no código da turma. Para este exemplo, retiramos o primeiro parâmetro. Isso volta como um tipo.
  • A linha 2 lança o último tipo retornado para uma classe. Isso é seguro porque sabemos quais tipos nossos parâmetros de tipo genérico são capazes de assumir e podem confirmar que todos eles serão classes (não sei como, em Java, é necessário obter um parâmetro genérico que não tenha uma instância de classe associado a ele, na verdade).

... e isso é muito bonito. Por isso, inserimos informações de tipo de nossa própria implementação concreta em nós mesmos e as usamos para acessar um identificador de classe. podemos dobrar o getGenericSuperclass () e ir dois níveis, ou eliminar getGenericSuperclass () e obter valores para nós mesmos como um tipo concreto (ressalva: eu não testei esses cenários, eles ainda não apareceram para mim).

Fica complicado se os seus filhos concretos são um número arbitrário de saltos de distância, ou se você é concreto e não final, e especialmente complicado se você espera que algum de seus filhos (variavelmente profundos) tenha seus próprios genéricos. Mas normalmente você pode projetar em torno dessas considerações, então isso faz com que você consiga a maior parte do caminho.

Espero que isso tenha ajudado alguém! Eu reconheço este post é antigo. Eu provavelmente vou cortar essa explicação e guardá-la para outras perguntas.


Isso é impossível porque os genéricos em Java são considerados apenas em tempo de compilação. Assim, os genéricos Java são apenas algum tipo de pré-processador. No entanto, você pode obter a classe real dos membros da lista.


Não, isso não é possível. Devido a problemas de compatibilidade com versões anteriores, os genéricos do Java são baseados em eliminação de tipos , em tempo de execução, tudo que você tem é um objeto List não-genérico. Há algumas informações sobre os parâmetros de tipo em tempo de execução, mas ele reside nas definições de classe (por exemplo, você pode perguntar " que tipo genérico essa definição de campo usa? "), Não em instâncias de objeto.


Na verdade eu tenho isso para trabalhar. Considere o seguinte trecho:

Method m;
Type[] genericParameterTypes = m.getGenericParameterTypes();
for (int i = 0; i < genericParameterTypes.length; i++) {
     if( genericParameterTypes[i] instanceof ParameterizedType ) {
                Type[] parameters = ((ParameterizedType)genericParameterTypes[i]).getActualTypeArguments();
//parameters[0] contains java.lang.String for method like "method(List<String> value)"

     }
 }

Estou usando o jdk 1.6


Por causa do tipo de eliminação, a única maneira de saber o tipo da lista seria passar o tipo como um parâmetro para o método:

public class Main {

    public static void main(String[] args) {
        doStuff(new LinkedList<String>(), String.class);

    }

    public static <E> void doStuff(List<E> list, Class<E> clazz) {

    }

}

Um constructo, uma vez eu me deparei com aparência

Class<T> persistentClass = (Class<T>)
   ((ParameterizedType)getClass().getGenericSuperclass())
      .getActualTypeArguments()[0];

Então, parece haver algum reflexo mágico ao redor que eu infelizmente não entendo completamente ... Desculpe.


Usar:

Class<?> typeOfTheList = aListWithTypeSpiderMan.toArray().getClass().getComponentType();

Você pode obter o tipo de um parâmetro genérico com reflexão, como neste exemplo que encontrei here :

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class Home<E> {
    @SuppressWarnings ("unchecked")
    public Class<E> getTypeParameterClass(){
        Type type = getClass().getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) type;
        return (Class<E>) paramType.getActualTypeArguments()[0];
    }

    private static class StringHome extends Home<String>{}
    private static class StringBuilderHome extends Home<StringBuilder>{}
    private static class StringBufferHome extends Home<StringBuffer>{}   

    /**
     * This prints "String", "StringBuilder" and "StringBuffer"
     */
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        Object object0 = new StringHome().getTypeParameterClass().newInstance();
        Object object1 = new StringBuilderHome().getTypeParameterClass().newInstance();
        Object object2 = new StringBufferHome().getTypeParameterClass().newInstance();
        System.out.println(object0.getClass().getSimpleName());
        System.out.println(object1.getClass().getSimpleName());
        System.out.println(object2.getClass().getSimpleName());
    }
}




reflection