Generics

Generics tornam o código mais seguro e flexível. Esta lição cobre o uso de generics em Java.

O que são Generics

Generics são tipos parametrizados que permitem que classes e métodos trabalhem com múltiplos tipos.

Benefícios dos Generics

Benefício Descrição
Segurança de tipo Verificação de tipo em tempo de compilação
Reutilização de código Um código funciona com múltiplos tipos
Eliminar casting Não é necessária conversão manual de tipos

Classes Genéricas

Sintaxe

JAVA
public class NomeDaClasse<T> {
    // Usar T como tipo
}

Exemplo: Classe Genérica

JAVA
public class Box<T> {
    private T value;
    
    public void set(T value) {
        this.value = value;
    }
    
    public T get() {
        return value;
    }
    
    public static void main(String[] args) {
        // Armazenar Integer
        Box<Integer> intBox = new Box<>();
        intBox.set(100);
        int num = intBox.get();  // Sem casting necessário
        
        // Armazenar String
        Box<String> strBox = new Box<>();
        strBox.set("Hello");
        String str = strBox.get();
        
        System.out.println("Box Integer: " + num);
        System.out.println("Box String: " + str);
    }
}
▶ Experimente

Múltiplos Parâmetros Genéricos

JAVA
public class Pair<K, V> {
    private K key;
    private V value;
    
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }
    
    public K getKey() { return key; }
    public V getValue() { return value; }
    
    @Override
    public String toString() {
        return key + "=" + value;
    }
    
    public static void main(String[] args) {
        Pair<String, Integer> pair = new Pair<>("Alice", 95);
        System.out.println(pair);  // Alice=95
    }
}

Métodos Genéricos

Sintaxe

JAVA
public static <T> nomeMetodo(parâmetros) {
    // Usar T
}

Exemplo: Métodos Genéricos

JAVA
public class GenericMethod {
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }
    
    public static <T> T getFirst(T[] array) {
        if (array == null || array.length == 0) {
            return null;
        }
        return array[0];
    }
    
    public static <T extends Comparable<T>> T getMax(T[] array) {
        T max = array[0];
        for (T element : array) {
            if (element.compareTo(max) > 0) {
                max = element;
            }
        }
        return max;
    }
    
    public static void main(String[] args) {
        Integer[] nums = {1, 2, 3, 4, 5};
        String[] names = {"Alice", "Bob", "Charlie"};
        
        printArray(nums);    // 1 2 3 4 5
        printArray(names);   // Alice Bob Charlie
        
        System.out.println("Primeiro número: " + getFirst(nums));  // 1
        System.out.println("Primeiro nome: " + getFirst(names));  // Alice
        
        System.out.println("Máximo: " + getMax(nums));  // 5
        System.out.println("Nome máximo: " + getMax(names));  // Charlie
    }
}
▶ Experimente

Interfaces Genéricas

Definindo Interface Genérica

JAVA
public interface Repository<T> {
    void add(T item);
    T get(int index);
    List<T> getAll();
}

Implementando Interface Genérica

JAVA
public class ArrayListRepository<T> implements Repository<T> {
    private List<T> list = new ArrayList<>();
    
    @Override
    public void add(T item) {
        list.add(item);
    }
    
    @Override
    public T get(int index) {
        return list.get(index);
    }
    
    @Override
    public List<T> getAll() {
        return new ArrayList<>(list);
    }
    
    public static void main(String[] args) {
        Repository<String> repo = new ArrayListRepository<>();
        repo.add("Alice");
        repo.add("Bob");
        System.out.println(repo.getAll());  // [Alice, Bob]
    }
}

Curingas Genéricos

Curinga Não Limitado ?

JAVA
public static void printList(List<?> list) {
    for (Object item : list) {
        System.out.println(item);
    }
}

Curinga Superior Limitado ? extends T

JAVA
// Aceita apenas Number e suas subclasses
public static double sum(List<? extends Number> list) {
    double total = 0;
    for (Number num : list) {
        total += num.doubleValue();
    }
    return total;
}

Curinga Inferior Limitado ? super T

JAVA
// Aceita apenas Integer e suas superclasses
public static void addNumbers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
    list.add(3);
}

Exemplo: Curingas

JAVA
import java.util.ArrayList;
import java.util.List;

public class WildcardDemo {
    // Curinga não limitado
    public static void printList(List<?> list) {
        for (Object item : list) {
            System.out.print(item + " ");
        }
        System.out.println();
    }
    
    // Curinga superior limitado
    public static double sum(List<? extends Number> list) {
        double total = 0;
        for (Number num : list) {
            total += num.doubleValue();
        }
        return total;
    }
    
    public static void main(String[] args) {
        List<Integer> ints = new ArrayList<>();
        ints.add(1);
        ints.add(2);
        ints.add(3);
        
        List<String> strs = new ArrayList<>();
        strs.add("Alice");
        strs.add("Bob");
        
        printList(ints);  // 1 2 3
        printList(strs);  // Alice Bob
        
        System.out.println("Soma: " + sum(ints));  // 6.0
    }
}
▶ Experimente

Type Erasure

Generics em Java são implementados através de type erasure—informações genéricas são apagadas após a compilação.

JAVA
// Antes da compilação
List<String> list = new ArrayList<>();
list.add("Hello");

// Depois da compilação (type erasure)
List list = new ArrayList();
list.add("Hello");
String str = (String) list.get(0);  // Casting automático
⚠️ Nota: Informações de tipo genérico não podem ser obtidas em tempo de execução. List<String> e List<Integer> são o mesmo tipo em tempo de execução.

Restrições Genéricas

Exemplo: Generics Limitados

JAVA
// T deve implementar Comparable
public static <T extends Comparable<T>> T min(T a, T b) {
    return a.compareTo(b) <= 0 ? a : b;
}

// T deve ser uma subclasse de Number
public static <T extends Number> double doubleValue(T num) {
    return num.doubleValue();
}
▶ Experimente

Nomes Comuns de Generics

Nome Significado
T Type (Tipo)
E Element (Elemento)
K Key (Chave)
V Value (Valor)
N Number (Número)

❓ Perguntas Frequentes

P: Por que usar generics? R: Segurança de tipo, reutilização de código e eliminação de casting.

P: Generics suportam tipos primitivos? R: Não, use classes wrapper. Por exemplo, List<int> deve ser List<Integer>.

P: Qual é a diferença entre <?> e <Object>? R: <?> é tipo desconhecido—só pode ler, não escrever. <Object> é tipo Object—pode ler e escrever.

📖 Resumo

📝 Exercícios

  1. Contêiner genérico: Implemente uma classe Stack genérica
  2. Método genérico: Implemente um método genérico para converter array em List
  3. Generics limitados: Implemente um método para comparar dois objetos

Próxima Lição

Na próxima lição, aprenderemos sobre Prática: Aplicações de Coleções — aplicando conhecimentos de coleções.

100%