Preciso identificar o tipo de uma coleção, mas não estou conseguindo. Por exemplo, tenho um método que recebe um List, ou seja, um tipo genérico. Dentro do método precisaria identificar qual o tipo da coleção entre alguns tipos definidos para dar o tratamento adequado. Tentei utilizando o instanceOf, mas não permite. Há alguma alternativa?
Como assim, não permite instanceOf?
tentou typecast?
Infelizmente não é possível determinar o tipo T de um List - isso é uma consequência do “type erasure”:
http://download.oracle.com/javase/1,5.0/docs/guide/language/generics.html -
As linhas 12 e 14 não compilam. Eu preciso identificar se o tipo recebido no método verificaTipo é um List ou um List
[code]public class TesteGenerics {
@Test
public void teste() {
List<User> user = new ArrayList<User>();
verificaTipo(user);
}
static <T> void verificaTipo(List<T> list) {
if (list instanceof List<User>) {
System.out.println("user!");
} else if (list instanceof List<Group>) {
System.out.println("grupo!");
} else {
System.out.println("nothing!");
}
}
}
[/code]
Seria isso?
Class classe = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
[quote=raf4ever]Seria isso?
Class classe = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
[/quote]
Desculpe a ignorância, mas não entendi… :shock:
[quote=ddso][quote=raf4ever]Seria isso?
Class classe = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
[/quote]
Desculpe a ignorância, mas não entendi… :shock: [/quote]
Também não entendi o que o método getGenericSuperclass faz
Mas sei o que ele não faz, que é retornar o tipo de uma variável declarada com generic, como você queria.
(Em testes que fiz aqui ele retorna o "E" do tipo parametrizado, como já disse não entendi ainda para que serve)
O motivo foi explicado pelo entaglement, os tipos declarados no < > servem apenas para validações no momento da compilação e não existem em tempo de execução. Não há nada que se possa fazer a respeito, me desculpe
O que dá para fazer no seu caso é verificar o tipo do primeiro elemento da lista. Se não tiver nenhum elemento provavelmente não tem problema, pois não haverá mesmo nada a fazer. E é claro, tome as devidas preocupações para não aparecer uma lista marota contendo mais de um tipo de elemento.
Se olharem o Generics Tutorial, vão ver que ele sugere exatamente o que fiz abaixo (passar um parâmetro adicional).
Olhem em:
http://download.oracle.com/javase/tutorial/extra/generics/index.html
package guj;
import java.util.ArrayList;
import java.util.List;
public class TesteListT {
public static <T> void fazerAlgo (List<T> lista, Class<T> klass) {
if (Integer.class.isAssignableFrom(klass)) {
System.out.println ("Isto é uma lista de inteiros.");
} else if (String.class.isAssignableFrom(klass)) {
System.out.println ("Isto é uma lista de strings.");
} else {
System.out.println ("Isto é uma lista de " + klass.getName());
}
}
/**
* @param args
*/
public static void main(String[] args) {
List<Integer> ints = new ArrayList<Integer>();
List<String> strings = new ArrayList<String>();
List<Number> numbers = new ArrayList<Number>();
fazerAlgo (ints, Integer.class); // Isto é uma lista de inteiros.
fazerAlgo (strings, String.class); // Isto é uma lista de strings.
fazerAlgo (numbers, Number.class); // Isto é uma lista de java.lang.Number
// A seguinte linha provocaria um erro de compilação,
// caso removêssemos o comentário, pois numbers é List<Number>, ou seja, T = Number, que é diferente de String.
// fazerAlgo (numbers, String.class);
}
}
Note que não se usa “instanceof” (no sentido que se A instanceof B, então A é um objeto que é uma instância de uma classe B).
Usa-se nesse caso "isAssignableFrom (se B.class.isAssignableFrom (A.class), então A é uma subclasse ou subinterface de B. ) - note que a ordem é inversa ao do instanceof.
[quote=entanglement]
…
fazerAlgo (ints, Integer.class); // Isto é uma lista de inteiros.
fazerAlgo (strings, String.class); // Isto é uma lista de strings.
fazerAlgo (numbers, Number.class); // Isto é uma lista de java.lang.Number
…
[/code][/quote]
Mas nesse caso, eu praticamente estou dizendo qual é o tipo da classe e passando-o como parâmetro!!!
Eu achei a ideia do gomesrod a mais adequada, alguem tem outra ideia?
Dê mais uma olhada. Adaptei o programa ( e só para lhe encher o saco, digo que “ParameterizedType” etc. não é muito útil para o seu caso em particular.)
package guj;
import java.lang.reflect.ParameterizedType;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class TesteListT {
public static <T> void fazerAlgo (List<T> lista, Class<T> klass) {
if (Integer.class.isAssignableFrom(klass)) {
System.out.println ("Isto é uma lista de inteiros.");
} else if (String.class.isAssignableFrom(klass)) {
System.out.println ("Isto é uma lista de strings.");
} else {
System.out.println ("Isto é uma lista de " + klass.getName());
}
}
public static <T> void fazerAlgo (List<T> lista) {
if (lista.size() > 0) {
Object obj = lista.get(0);
System.out.println ("Esta é uma lista de objetos, a classe do primeiro objeto é: " + obj.getClass().getName());
} else {
System.out.println ("Esta é uma lista vazia.");
}
}
/**
* @param args
*/
public static void main(String[] args) {
List<Integer> ints = new ArrayList<Integer>();
List<String> strings = new ArrayList<String>();
List<Number> numbers = new ArrayList<Number>();
numbers.add(BigInteger.valueOf (100));
fazerAlgo (ints, Integer.class); // Isto é uma lista de inteiros.
fazerAlgo (strings, String.class); // Isto é uma lista de strings.
fazerAlgo (strings); // Imprime "Esta é uma lista vazia."
fazerAlgo (numbers, Number.class); // Isto é uma lista de java.lang.Number
fazerAlgo (numbers); // Imprime "Esta é uma lista de objetos, a classe do primeiro objeto é: java.math.BigInteger"
// A seguinte linha provocaria um erro de compilação,
// caso removêssemos o comentário, pois numbers é List<Number>, ou seja, T = Number, que é diferente de String.
// fazerAlgo (numbers, String.class);
// Note que a "type erasure" realmente faz com que o parâmetro da classe se perca em tempo de execução
System.out.println (ints.getClass().getCanonicalName()); // java.util.ArrayList
System.out.println (strings.getClass().getCanonicalName()); // java.util.ArrayList
System.out.println (strings.getClass().getCanonicalName()); // java.util.ArrayList
// A seguinte linha imprime unica e tão somente "[E]", que é o nome do parâmetro da classe genérica.
// Não imprime mais nada.
System.out.println (Arrays.asList(((ParameterizedType)ints.getClass().getGenericSuperclass()).getActualTypeArguments()));
}
}
Pois é, você está realmente dizendo qual é o tipo do parâmetro da classe parametrizada.