Solucionado (ver solução)
Solucionado
(ver solução)
2
respostas

Exercício Polimorfismo com Generics

Olá. Como exercício estou tentando criar uma lista encadeada de forma manual que aceite qualquer tipo de objeto. Só não estou conseguindo retornar meus objetos inseridos:


public class Principal {
    public static <T> void main(String[] args) {
        ListaEncadeada<String> lista = new ListaEncadeada<>();
        String teste1 = "var 1";
        String teste2 = "var 2";
        String teste3 = "var 3";

        lista.add(teste1);
        lista.add(teste2);
        lista.add(teste3);

        String dados[] = lista.getObjeto();
        for (String string : dados) {
            System.out.println(string);
        }

    }
}

public class ListaEncadeada<T> {
    private Nodo inicio;
    private Nodo fim;
    private int tamanho = 0;
    private int capacidade = 100;

    public class Nodo{
        Nodo nodoProximo;
        private T info;

        public Nodo(T obj){
            this.info = obj;
        }

        public void setProximo(Nodo proximo){
            this.nodoProximo = proximo;
        }

        public Nodo getProximo(){
            return nodoProximo;
        }

        public T getInfo(){
            return this.info;
        }

        public void setInfo(T info) {
            this.info = info;
        }
    }

    public void add(T obj){
        if (this.inicio == null) {
            Nodo nodo = new Nodo(obj);
            this.inicio = nodo;
            this.tamanho += 1;
        }else if (this.fim ==null) {
            Nodo nodo = new Nodo(obj);
            this.inicio.setProximo(nodo);
            this.fim = nodo;
            this.tamanho += 1;
        }else {
            Nodo nodo = new Nodo(obj);
            this.fim.setProximo(nodo);
            this.fim = nodo;
            this.tamanho += 1;
        }
    }

    public void remove(T obj){
        Nodo nodoTest;
        if (inicio.getInfo() == obj) { //se o que quer excluir foi o inicio
            inicio = inicio.getProximo();

        }else if (fim.getInfo() == obj) { // se for o fim
            for (int i = 0; i < this.tamanho; i++) {
                nodoTest = inicio;
                if (nodoTest.getProximo()==fim) {
                    nodoTest.getProximo().setInfo(null);
                    nodoTest.setProximo(nodoTest.getProximo().getProximo());    
                }
            }
        }else{                            // se for no meio
            if (inicio.getProximo()==fim) { // se só tem 2 objetos
                fim = inicio;
                inicio.setProximo(null);
            }else {
                for (int i = 0; i < this.tamanho; i++) {
                    nodoTest = inicio;
                    if (nodoTest.getProximo().getInfo() == obj) {
                        nodoTest.getProximo().setInfo(null);
                        nodoTest.setProximo(nodoTest.getProximo().getProximo());
                    }
                    nodoTest = nodoTest.getProximo();
                }
            }
        }
    }

    public T[] getObjeto(){
        T[] retorno = null;    //O problema está aqui :(
        Nodo nodo = this.inicio;
        for (int i = 0; i < this.tamanho; i++) {
            retorno[i] = nodo.getInfo();
            nodo = nodo.getProximo();
        }
        return retorno;

    }
}
2 respostas
solução!

Oi Gustavo, tudo certo?

Como provavelmente você já percebeu, não rola instanciar um Array de um tipo genérico:

T[] retorno = new T[this.tamanho]; // compilador reclama

O new só será executado para criar o objeto na memória em tempo de execução, quando a aplicação estiver rodando. O problema é que em execução você não tem mais o tipo genérico T. :sadpanda:

Isso ocorre por conta do type erasure. Basicamente, o java irá checar os tipos genéricos em tempo de compilação, para garantir que ao criar um:

List<String> lista = new ArrayList<>();

Caso você tente fazer um:

lista.add(new Conta()); // lista de String. Não compila

isso não compile.

Mas em seguida ele removerá o tipo genérico na compilação. Substituindo os lugares que usam os parâmetros genéricos por Object. Um dos motivos é que quando seu bytecode será compatível com JVMs antigas que não suportam generics.

Ou seja, após a compilação, no seu bytecode, você vai acabar com algo parecido com:

public void add(Object obj){
}

A consequência, é que você não consegue fazer um

new T[]

porque não tem mais como saber quem é T em execução, quando você chama o new.

--

Olhando a documentação da LinkedList do java, o método toArray(), que não recebe nada como parâmetro, retorna um array de Object[].

No seu código, ficaria mais ou menos assim:

public Object[] getArrayDeObjetos(){
    Object[] retorno = new Object[this.tamanho];

    Nodo nodo = this.inicio;

    for (int i = 0; i < this.tamanho; i++) {
        retorno[i] = nodo.getInfo();
        nodo = nodo.getProximo();
    }

    return retorno;
}

E no método main, após ter criado a lista e adicionado os valores:

Object dados[] = lista.getArrayDeObjetos();

for (Object dado : dados) {
    System.out.println(dado);
}

Existe uma sobrecarga do método toArray(T[] a) que recebe um array do tipo genérico T como parâmetro.

Aqui, dando uma ajudinha para o Java, passando um array do mesmo tipo da lista, você consegue criar um array a partir da classe java.lang.reflect.Array:

public T[] getArrayDeObjetos(T[] array){
    T[] retorno = (T[]) Array.newInstance(array.getClass().getComponentType(), this.tamanho);

    Nodo nodo = this.inicio;

    for (int i = 0; i < this.tamanho; i++) {
        retorno[i] = nodo.getInfo();
        nodo = nodo.getProximo();
    }

    return retorno;
}

Agora sim o seu retorno será um array de String . Mas para isso você precisa passar um array em runtime para ele conseguir obter o tipo. Então no método main

String dados[] = lista.getArrayDeObjetos(new String[0]);

for (String dado : dados) {
    System.out.println(dado);
}

}

Faz sentido? Abraço!

Faz todo o sentido! Funcionou.