Generics em Java: para que servem e como usar para iniciantes
Ao estudar Java, mais cedo ou mais tarde você encontrará o conceito de Generics (genéricos, generalizações). Para um iniciante, isso pode parecer complicado, mas na verdade é uma das ferramentas mais úteis da linguagem. Neste artigo, vamos entender o que são Generics, para que servem e como usá-los corretamente. O material é voltado para desenvolvedores iniciantes.
O que são Generics e por que é ruim sem eles?
Generics (generalizações) é um mecanismo do Java que permite parametrizar tipos. Em palavras simples, você pode escrever uma classe, interface ou método que funcionará com diferentes tipos de dados, mas mantendo a tipagem estrita em tempo de compilação.
Imagine que você quer criar uma caixa na qual possa colocar qualquer objeto. Sem Generics, você teria que usar Object:
public class Box { private Object item;
public void put(Object item) { this.item = item; }
public Object get() { return item; }}Usar essa classe é um verdadeiro tormento:
Box box = new Box();box.put("Hello");// Ao extrair, é necessário fazer uma conversão explícita de tipoString text = (String) box.get(); // Risco de ClassCastException!Se você acidentalmente colocar um número e depois tentar convertê-lo para string — o programa falhará com um erro em tempo de execução. Generics resolvem esse problema, transferindo a verificação de tipos para o tempo de compilação.
Como usar Generics: sintaxe básica
Vamos reescrever nossa classe Box usando Generics:
public class Box<T> { private T item;
public void put(T item) { this.item = item; }
public T get() { return item; }}Aqui, <T> é o parâmetro de tipo. T é um nome convencional (da palavra "Type"), mas você pode usar qualquer letra ou palavra: E (Element), K (Key), V (Value), etc.
Agora o uso se tornou seguro e elegante:
Box<String> stringBox = new Box<>();stringBox.put("Hello");String text = stringBox.get(); // Sem necessidade de conversão!
// Esta linha não será compilada:// stringBox.put(123); // Erro de compilação!O compilador sabe que stringBox funciona apenas com strings. Se você tentar colocar um número, o código nem será compilado — você saberá do erro imediatamente, e não em tempo de execução.
Generics em coleções: onde são mais necessários
O local mais comum de uso de Generics são as coleções do Java (List, Set, Map e outras). Antes do surgimento dos genéricos (no Java 5), os desenvolvedores sofriam com ArrayList, que armazenava Object:
// Estilo antigo (sem Generics)List list = new ArrayList();list.add("Olá");list.add(123); // Isso também pode ser adicionado!
String item = (String) list.get(0); // NormalString item2 = (String) list.get(1); // ClassCastException!Com Generics, tudo é mais simples e seguro:
List<String> names = new ArrayList<>();names.add("Alice");names.add("Bob");// names.add(123); // Erro de compilação
for (String name : names) { System.out.println(name.toUpperCase()); // Sem conversões!}O mesmo funciona para Map:
Map<String, Integer> ageMap = new HashMap<>();ageMap.put("Alice", 30);ageMap.put("Bob", 25);
int aliceAge = ageMap.get("Alice"); // Desempacotamento automático intRestrições e wildcards: como tornar Generics flexíveis
Às vezes, você precisa restringir quais tipos um genérico pode aceitar. Para isso, são usados bounded type parameters (parâmetros de tipo limitados).
Exemplo