[Resolvido] Como pegar o tipo de uma coleção genérica?

16 respostas
V

Por exemplo:

Tenho a classe Historico, e as classes HistoricoAluno e HistoricoProfessor que estendem Historico…

No runtime posso passar uma ou outra assim:

Set<? extends Historico> colecaoDeHistoricos = new HashSet<HistoricoAluno>([Passa uma collection aqui]);

Só que gostaria de manipular “colecaoDeHistoricos” de acordo com o tipo, se fosse HistoricoAluno fazia uma coisa, se fosse HistoricoProfessor fazia outra…

Alguém ajuda? Obrigado. :smiley:

16 Respostas

G

Você pode iterar sobre a coleção e ir fazendo teste de instanceOf dos itens.

N

Vin?ius Abreu de Fran?:
Por exemplo:

Tenho a classe Historico, e as classes HistoricoAluno e HistoricoProfessor que estendem Historico…

No runtime posso passar uma ou outra assim:

Set<? extends Historico> colecaoDeHistoricos = new HashSet<HistoricoAluno>([Passa uma collection aqui]);

Só que gostaria de manipular “colecaoDeHistoricos” de acordo com o tipo, se fosse HistoricoAluno fazia uma coisa, se fosse HistoricoProfessor fazia outra…

Alguém ajuda? Obrigado. :smiley:

Parece um pouco tosco, mas talvez seja uma solução que pensei rapidamente. A lista será somente de um objeto, certo ?
Podes pegar o objeto da primeira posição e verificar via instance of a qual classe pertence, depois, é iterar sobre a lista de acordo com o tipo que desejas.

V

Só teria essa maneira? Isso me cheira mal… Tipo, não posso pegar diretamente o tipo da coleção já que sei que todos os itens são do mesmo tipo…

Tentei:

//Quero saber se o Set colecaoDeHistoricos é do tipo Set<HistoricoAluno>
colecaoDeHistoricos.getClass().isAssignableFrom(HistoricoAluno.class)

Mas nada… :roll:

D

Há uma opção aqui


e outra aqui http://stackoverflow.com/questions/1942644/get-generic-type-of-java-util-list que dizem a mesma coisa no final, usar reflection.

M
public void method(Collection<? extends Historico> collection) {

		for (Historico historico : collection) {
			if (historico.getClass().isAssignableFrom(HistoricoProfessor.class)) {
				// invoked method for Alunos
			} else {
				// invoked method for Professores
			}
		}
	}
R

Vin?ius Abreu de Fran?:
Só teria essa maneira? Isso me cheira mal… Tipo, não posso pegar diretamente o tipo da coleção já que sei que todos os itens são do mesmo tipo…

Tentei:

//Quero saber se o Set colecaoDeHistoricos é do tipo Set<HistoricoAluno>
colecaoDeHistoricos.getClass().isAssignableFrom(HistoricoAluno.class)

Mas nada… :roll:

A princípio, isso não é possível, pois a informação de tipo genérico é removida após a compilação. Agora, se você precisa de um comportamento que varia com o tipo do objeto, esse comportamento tem que ser implementado dentro da classe, e não fora dela. Pesquise sobre o padrão Strategy.

L

Usa um vetor de objetos

D

Try it:

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

public class Test {

    List<String> stringList = new ArrayList<String>();
    List<Integer> integerList = new ArrayList<Integer>();

    public static void main(String... args) throws Exception {
        Field stringListField = Test.class.getDeclaredField("stringList");
        ParameterizedType stringListType = (ParameterizedType) stringListField.getGenericType();
        Class<?> stringListClass = (Class<?>) stringListType.getActualTypeArguments()[0];
        System.out.println(stringListClass); // class java.lang.String.

        Field integerListField = Test.class.getDeclaredField("integerList");
        ParameterizedType integerListType = (ParameterizedType) integerListField.getGenericType();
        Class<?> integerListClass = (Class<?>) integerListType.getActualTypeArguments()[0];
        System.out.println(integerListClass); // class java.lang.Integer.
    }
}

And tell us what`s the result…

L

[code] if(seuobjeto intanceof seutipo)
{}

N

drsmachado:
Há uma opção aqui

e outra aqui http://stackoverflow.com/questions/1942644/get-generic-type-of-java-util-list que dizem a mesma coisa no final, usar reflection.

Ambas soluções retornam um class, o que não mostra qual classe é. De qualquer forma, tem que verificar a classe.
Ainda acho que verificar via instance of o primeiro objeto e a partir dele identificar tua necessidade é uma boa solução.

Agora, se quer melhorar isso, é adotar a ideia do rmendes08, é uma alternativa e possivelmente mais elegante.
Podes usar a solução de reflection, é claro. Podes criar um método que te retorna um Enum ou algo do tipo indicando qual classe é, é uma solução também.

L

[code]
public object Queisso(object obj)
{
tipo1 tp1,tipo2 tp2;

if(obj instance of tipo1)
{
tp1=obj;
return tp1;
}
else
{
tp2=obj;
return tp2;
}

}

V

Olá pessoal, obrigado pelas respostas, já tentei as outras soluções e nada, acho que a do drsmachado (usar reflection) se aproxima mais do que eu quero fazer. Sobre o padrão Strategy, acho que não se encaixaria aqui no contexto e mesmo que o fizesse, em algum momento teria que fazer a comparação de tipos… Vou tentar usar reflection e posto e resultado…

*Só esclarecendo a outra solução postada por mauriciot.silva, não quero iterar na lista, quero saber de que tipo é para adicionar mais um elemento e setar no bean do tipo correto. (Uma Cidade possui uma lista de históricos de Alunos e Professores, tenho que saber em qual lista adicionar)

L

“Só teria essa maneira? Isso me cheira mal… Tipo, não posso pegar diretamente o tipo da coleção já que sei que todos os itens são do mesmo tipo…”

como assim ?

R

drsmachado:
Try it:

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

public class Test {

    List<String> stringList = new ArrayList<String>();
    List<Integer> integerList = new ArrayList<Integer>();

    public static void main(String... args) throws Exception {
        Field stringListField = Test.class.getDeclaredField("stringList");
        ParameterizedType stringListType = (ParameterizedType) stringListField.getGenericType();
        Class<?> stringListClass = (Class<?>) stringListType.getActualTypeArguments()[0];
        System.out.println(stringListClass); // class java.lang.String.

        Field integerListField = Test.class.getDeclaredField("integerList");
        ParameterizedType integerListType = (ParameterizedType) integerListField.getGenericType();
        Class<?> integerListClass = (Class<?>) integerListType.getActualTypeArguments()[0];
        System.out.println(integerListClass); // class java.lang.Integer.
    }
}

And tell us what`s the result…

Eu fiz um teste aqui e essa abordagem não resolve o problema:

1 - você só consegue fazer essa inferência a partir de variáveis de instância

2 - nem sempre o tipo retornado por getActualTypeArguments é uma classe. No caso, de declarar o seu Set como Set<? extends Historico> vai retornar um WildcardType.

Ou seja, mesmo essa solução só te permite obter tipos conhecidos em tempo de compilação, mas você não consegue obter o tipo passado como argumento na criação do HashSet.

Enfim, eu consideraria seriamente repensar esse design enquanto houver tempo, caso contrário, esse tipo de workaround vai se espalhar rapidamente pelo seu sistema.

Minha sugestão: o bom e velho composição a favor de herança. Por que ao invés de estender Historico, você não mantém uma única classe Historico com um campo de tipo e um campo objeto para receber tanto Aluno quanto Professor ? Ou então, você definitivamente separa HistoricoAluno e HistoricoProfessor, sem relação de herança entre elas …

R

deixa a propria subclasse decidir o que fazer

assim:

public class Main{
      public void rodar(){
              Historico h;
              Cidade cidade=new Cidade();

              h=new Historico();
              h.adicionarMe(cidade);

              h=new HistoricoAluno();
              h.adicionarMe(cidade);


              h=new HistoricoProfessor();
              h.adicionarMe(cidade);           

      }
}

public class Cidade{
          private Set<? extends Historico> listaPadrao;
          private Set<? extends Historico> listaProfessor;
          private Set<? extends Historico> listaAluno;

    public Cidade(){
              listaPadrao=new HashSet<? extends Historico>();
              listaProfessor=new HashSet<? extends Historico>();
              listaAluno=new HashSet<? extends Historico>();
    }

     public Set<? extends Historico> getListaAluno(){
             return this.listaAluno;
     }

     public Set<? extends Historico> getListaProfessor){
             return this.listaProfessor;
     }

     public Set<? extends Historico> getListaPadrao(){
             return this.listaPadrao;
     }

}

public class Historico{
        public void adicionarMe(Cidade cidade){             
                     cidade.getListaPadrao().put(this);
        }
}



public class HistoricoAluno extends Historico{
        public void adicionarMe(Cidade cidade){
                    cidade.getListaAluno().put(this);
        }
}

public class HistoricoProfessor  extends Historico{
        public void adicionarMe(Cidade cidade){
                   cidade.getListaProfessor().put(this);
        }
}

Posta o seu codigo se a solucao nao for suficiente.

V

Olá amigos do GUJ, desculpem a demora pra responder já que já tinha conseguido resolver com uma solução que adotei na mesma tarde (final de semana, mais tempo pra responde heheh)…
Bem pessoal, depois de testar as várias soluções apontadas resolvi escolher a mais simples e NÃO a considero como POG já que só posso escolher entre um tipo e outro e resolveu perfeitamente o problema. O jeito foi verificar o tipo do item que vem contido na lista pra saber o tipo da lista, solução de mauriciot.silva embora que modificada pois não vou percorrer os itens.

Aqui vai um exemplo de como fiz envolvendo as entidades Contador e Prefeito:

private void ordenaEAdicionaNaColecaoDeHistoricoGenerico(Set<? extends Historico> colecaoDeHistoricos, boolean isOrdemCrescente) {
        assert !colecaoDeHistoricos.isEmpty();
        if (colecaoDeHistoricos.toArray()[0] instanceof HistoricoContador) {
            TreeSet<HistoricoContador> ts;
            if (isOrdemCrescente) {
                ts = new TreeSet<HistoricoContador>(comparatorHistoricoOrdemCrescente);
            } else {
                ts = new TreeSet<HistoricoContador>(comparatorHistoricoOrdemDecrescente);
            }
            ts.addAll((Set<HistoricoContador>) colecaoDeHistoricos);
            cidade.setHistoricoContatores(ts);
        } else if (colecaoDeHistoricos.toArray()[0] instanceof HistoricoPrefeito) {
            TreeSet<HistoricoPrefeito> ts;
            if (isOrdemCrescente) {
                ts = new TreeSet<HistoricoPrefeito>(comparatorHistoricoOrdemCrescente);
            } else {
                ts = new TreeSet<HistoricoPrefeito>(comparatorHistoricoOrdemDecrescente);
            }
            ts.addAll((Set<HistoricoPrefeito>) colecaoDeHistoricos);
            cidade.setHistoricoPrefeitos(ts);
        }
    }

Obrigato a todos pela ajuda, valeu! :smiley:

Criado 6 de julho de 2012
Ultima resposta 7 de jul. de 2012
Respostas 16
Participantes 8