Da trilha de Java Este conceito ainda não saiu como card do dia. ir para o card de hoje

Java Genéricos Intermediário

Genéricos

Genéricos permitem escrever classes, interfaces e métodos que operam sobre tipos parametrizados, garantindo segurança de tipos em tempo de compilação e evitando casts explícitos. Eles foram introduzidos no Java 5 para resolver problemas de ClassCastException em coleções, por exemplo.

Com genéricos, você define uma classe Caixa<T> e pode criar Caixa<String> ou Caixa<Integer>, e o compilador verifica os tipos usados. Isso é possível porque o compilador insere os casts necessários e verifica os tipos em tempo de compilação, mas depois remove as informações de tipo (type erasure), então em tempo de execução não há sobrecarga de performance.

Além disso, você pode restringir os tipos com bounded types (<T extends Number>) e usar wildcards (? extends, ? super) para aumentar a flexibilidade em APIs. Entender esses conceitos é essencial para usar bem as coleções da biblioteca padrão e criar código reutilizável e seguro.

Pontos-chave

  • Genéricos permitem parametrizar classes e métodos com tipos, garantindo verificação em tempo de compilação.
  • Bounded types restringem o tipo genérico a subtipos de uma classe ou interface, permitindo chamar métodos desse bound.
  • Wildcards (? extends T e ? super T) tornam APIs mais flexíveis para leitura e escrita de coleções.
  • Type erasure remove os parâmetros de tipo em tempo de execução, então não é possível usar new T() ou instanceof T.
  • Métodos genéricos podem ser declarados independentemente da classe, com o parâmetro de tipo antes do retorno.
GenericosDemo.java
import java.util.ArrayList;
import java.util.List;

// Classe genérica simples
class Caixa<T> {
    private T conteudo;
    public void guardar(T item) { this.conteudo = item; }
    public T obter() { return conteudo; }
}

public class GenericosDemo {
    // Método genérico com bounded type
    public static <T extends Number> double soma(List<T> numeros) {
        double total = 0;
        for (T n : numeros) total += n.doubleValue();
        return total;
    }

    // Wildcard para leitura
    public static void imprimir(List<? extends Number> lista) {
        for (Number n : lista) System.out.print(n + " ");
        System.out.println();
    }

    public static void main(String[] args) {
        Caixa<String> caixaTexto = new Caixa<>();
        caixaTexto.guardar("Olá, genéricos!");
        System.out.println(caixaTexto.obter());

        List<Integer> inteiros = new ArrayList<>();
        inteiros.add(10);
        inteiros.add(20);
        System.out.println("Soma: " + soma(inteiros));

        imprimir(inteiros);
        List<Double> doubles = List.of(1.5, 2.5, 3.5);
        imprimir(doubles);
    }
}

Exercícios

  1. 1
    Fácil

    Par genérico

    Crie uma classe genérica Par<A, B> que armazena dois valores de tipos diferentes. Implemente métodos getPrimeiro(), getSegundo() e inverter() que retorna um novo Par<B, A> com os valores trocados. No main, crie um Par<String, Integer>, imprima os valores e depois o par invertido.

    Resolver
  2. 2
    Médio

    Método genérico com bounded type

    Escreva um método genérico maior que recebe uma lista de elementos que implementam Comparable<T> e retorna o maior elemento. O método deve ser public static <T extends Comparable<T>> T maior(List<T> lista). No main, teste com uma lista de Integer e uma de String, imprimindo o maior de cada.

    Resolver
  3. 3
    Desafio

    Wildcards e cópia de listas

    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.

    Resolver

Continue estudando