É 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>());
}
}
java
generics
reflection
cimnina
fonte
fonte
Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
não tenho certeza qual é a restrição.java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
Quero tentar detalhar a resposta do @DerMike para explicar:
Primeiro, o apagamento de tipo não significa que o JDK elimina informações de tipo em tempo de execução. É um método para permitir que a verificação do tipo em tempo de compilação e a compatibilidade do tipo em tempo de execução coexistam no mesmo idioma. Como esse bloco de código implica, o JDK retém as informações de tipo apagadas - elas não estão associadas a conversões e outras coisas verificadas.
Segundo, isso fornece informações de tipo genérico para uma classe genérica exatamente um nível acima da hierarquia do tipo concreto que está sendo verificado - ou seja, uma classe pai abstrata com parâmetros de tipo genérico pode encontrar os tipos concretos correspondentes aos seus parâmetros de tipo para uma implementação concreta de si mesma. que herda diretamente dele. Se essa classe não fosse abstrata e instanciada, ou a implementação concreta estivesse em dois níveis abaixo, isso não funcionaria (embora um pouco de brincadeira pudesse fazer com que se aplicasse a qualquer número predeterminado de níveis além de um ou até a classe mais baixa com X parâmetros de tipo genérico, etc.).
Enfim, para a explicação. Aqui está o código novamente, separado em linhas para facilitar a referência:
Sejam 'nós' a classe abstrata com tipos genéricos que contêm esse código. Lendo isso de dentro para fora:
... e é isso mesmo. 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. poderíamos duplicar o getGenericSuperclass () e subir dois níveis, ou eliminar o getGenericSuperclass () e obter valores para nós mesmos como um tipo concreto (ressalva: não testei esses cenários, eles ainda não vieram para mim).
Fica complicado se seus filhos concretos estão a 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 (com profundidade variada) tenha seus próprios genéricos. Mas você geralmente pode projetar em torno dessas considerações, e isso o leva a maior parte do caminho.
Espero que isso tenha ajudado alguém! Eu reconheço que este post é antigo. Provavelmente cortarei esta explicação e a guardarei para outras perguntas.
fonte
Na verdade, consegui que isso funcionasse. Considere o seguinte trecho:
Estou usando o jdk 1.6
fonte
Na verdade, existe uma solução, aplicando o truque "classe anônima" e as idéias dos Super Type Tokens :
O truque consiste na criação de uma classe anônima ,
new ArrayList<SpiderMan>() {}
, no lugar do original (simples)new ArrayList<SpiderMan>()
. O uso de uma classe anômala (se possível) garante que o compilador retenha informações sobre o argumento de tipoSpiderMan
fornecido ao parâmetro de tipoList<?>
. Voilà!fonte
Por causa do apagamento do tipo, a única maneira de saber o tipo da lista seria passar o tipo como parâmetro para o método:
fonte
Apêndice à resposta do @ 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):
fonte
Awesomeable<Beer>
. Nesse caso, as informações do tipo são preservadas. Se você passarnew Awesomable<Beer> ()
para o método, não funcionará.Awesomable<Beer>
-the-fly sem a definição explícita de uma subclasse concreta, comoEstrellaGalicia
neste caso, ainda está obtendo o tipo parametrizado: eu executei agora:System.out.println("Type is: " + new Awesomable<Beer>() {}.parameterizedType());
---> O tipo é: ParameterizedStuff $ BeerNão, isso não é possível. Devido a problemas de compatibilidade com versões anteriores, os genéricos do Java são baseados no apagamento de tipo , ou seja, no tempo de execução, tudo o que você tem é um
List
objeto não genérico . Há algumas informações sobre parâmetros de tipo em tempo de execução, mas ele reside nas definições de classe (ou seja, você pode perguntar " que tipo genérico a definição desse campo usa? "), Não nas instâncias de objetos.fonte
Conforme apontado por @bertolami, não é possível usar um tipo de variável e obter seu valor futuro (o conteúdo da variável typeOfList).
No entanto, você pode passar a classe como parâmetro desta forma:
Isso é mais ou menos o que o Google faz quando você precisa passar uma variável de classe para o construtor de ActivityInstrumentationTestCase2 .
fonte
Não, não é possível.
Você pode obter um tipo genérico de um campo, pois uma classe é a única exceção a essa regra e até isso é um pouco complicado.
Consulte Conhecendo o tipo de genérico em Java para obter um exemplo disso.
fonte
Você pode obter o tipo de um parâmetro genérico com reflexão, como neste exemplo que encontrei aqui :
fonte
A resposta rápida à pergunta é não, você não pode, devido ao apagamento do tipo genérico Java.
A resposta mais longa seria que, se você criou sua lista assim:
Nesse 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:
E como a extrapolação dos tipos genéricos de superclasses e super interfaces 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.
fonte
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.
fonte
Eu codifiquei isso para métodos que esperam aceitar ou retornar
Iterable<?...>
. Aqui está o código:fonte
Você não pode obter um parâmetro genérico de uma variável. Mas você pode, a partir de uma declaração de método ou de campo:
fonte
Só para mim, foi difícil ler esse trecho de código, apenas o dividi em duas linhas legíveis:
Eu queria criar uma instância do parâmetro Type sem ter nenhum parâmetro para o meu método:
fonte
Aqui está outro truque. Use uma matriz vararg genérica
fonte
Percebi que muitas pessoas se inclinam para a
getGenericSuperclass()
solução:No entanto, esta solução é propensa a erros. Ele vai não funcionar corretamente se existem genéricos nos descendentes. Considere isto:
Que tipo terá
Bar.persistentClass
?Class<Integer>
? Não, seráClass<Double>
. Isso acontecerá porquegetClass()
sempre retorna a classe mais alta,Bar
neste caso, e sua super classe genérica éFoo<Double>
. Portanto, o tipo de argumento seráDouble
.Se você precisar de uma solução confiável que não falhe, posso sugerir duas.
Guava
. Tem uma classe que foi feito exatamente para esta finalidade:com.google.common.reflect.TypeToken
. Ele lida com todos os casos de canto muito bem e oferece uma funcionalidade mais agradável. A desvantagem é uma dependência extra. Como você usou essa classe, seu código ficaria simples e claro, assim:fonte
Usar:
fonte
toArray()
retornaObject[]
. Você não pode e não deve depender de ser um subtipo específico.