Desafio Java
Wildcards e cópia de listas
Enunciado
Implemente um método genérico copiar que copia todos os elementos de uma lista origem para uma lista destino. A assinatura deve ser public static <T> void copiar(List<? extends T> origem, List<? super T> destino). O método deve adicionar cada elemento da origem ao destino. No main, crie uma List<Integer> origem e uma List<Number> destino, copie e imprima o destino. Explique por que os wildcards são necessários.
Requisitos
- A assinatura do método deve ser exatamente
public static <T> void copiar(List<? extends T> origem, List<? super T> destino). - O método deve percorrer a origem e adicionar cada elemento ao destino.
- O
maindeve demonstrar a cópia deList<Integer>paraList<Number>e imprimir o resultado.
Código inicial
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
// TODO: criar listas e chamar copiar
}
// TODO: implementar copiar
}
Saída esperada
Destino: [10, 20, 30]
Ver dica
Use ? extends T para a origem (produtor) e ? super T para o destino (consumidor). Lembre-se do princípio PECS.
Mostrar solução
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> origem = List.of(10, 20, 30);
List<Number> destino = new ArrayList<>();
copiar(origem, destino);
System.out.println("Destino: " + destino);
}
public static <T> void copiar(List<? extends T> origem, List<? super T> destino) {
for (T item : origem) {
destino.add(item);
}
}
}
Passo a passo
- O método
copiaré genérico e usa wildcards:? extends Tna origem e? super Tno destino. - O wildcard
? extends Tpermite que a origem seja uma lista de qualquer subtipo deT, então podemos ler elementos comoT. - O wildcard
? super Tpermite que o destino seja uma lista de qualquer supertipo deT, então podemos adicionar elementos do tipoT. - No loop, cada elemento da origem é lido como
Te adicionado ao destino. - No
main, criamosList<Integer>eList<Number>. A inferência de tipo defineTcomoInteger, e a chamada é válida porqueIntegeré subtipo deNumber. - O destino é uma
ArrayList<Number>que aceitaInteger. - Imprimimos o destino, que contém os elementos copiados.
Por que funciona
Os wildcards seguem o princípio PECS (Producer Extends, Consumer Super): a origem é um produtor de elementos (usamos extends), e o destino é um consumidor (usamos super). Isso permite que o método seja mais flexível do que usar apenas List<T> para ambos, pois aceita listas de subtipos e supertipos. A type erasure garante que em runtime não há sobrecarga, mas o compilador verifica a segurança.
Erros comuns
- Usar
List<T>para ambos os parâmetros: isso restringiria a chamada a listas do mesmo tipo exato, impedindoList<Integer>paraList<Number>. - Inverter os wildcards:
List<? super T> origemeList<? extends T> destinocausaria erro ao adicionar, pois não se pode adicionar a? extends T. - Tentar adicionar à origem:
origem.add(...)não compila porque? extends Té somente leitura. - Esquecer de importar
ArrayListeList.
Outra forma de resolver
Usar Collections.copy(destino, origem), mas ela exige que o destino tenha tamanho suficiente e não lida com wildcards da mesma forma. O método manual é mais didático.
Saída esperada
Destino: [10, 20, 30]