Identificar tipo do List<>

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&lt;T&gt;)
		((ParameterizedType)getClass().getGenericSuperclass())
		.getActualTypeArguments()[0];

[quote=raf4ever]Seria isso?

Class classe = (Class&lt;T&gt;) ((ParameterizedType)getClass().getGenericSuperclass()) .getActualTypeArguments()[0]; [/quote]

Desculpe a ignorância, mas não entendi… :shock:

[quote=ddso][quote=raf4ever]Seria isso?

Class classe = (Class&lt;T&gt;) ((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 :frowning:

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.