Os Generics foram introduzidos no Java 5 para permitir que classes, interfaces e métodos trabalhem com tipos parametrizados. Essa funcionalidade aumenta a segurança e a reutilização do código, reduzindo a necessidade de conversões explícitas e evitando erros em tempo de execução.
Neste artigo, exploraremos o conceito de Generics, como utilizá-los e como eles tornam o código mais robusto e flexível.
Por Que Usar Generics?
Resolva exercícios e atividades acadêmicas
Antes dos Generics, os desenvolvedores usavam coleções que armazenavam objetos como Object
. Isso exigia type casting ao recuperar elementos, o que era propenso a erros:
Sem Generics:
List lista = new ArrayList();
lista.add("Texto");
String s = (String) lista.get(0); // Casting necessário
Se um tipo incompatível fosse adicionado à lista, isso causaria um erro em tempo de execução:
Encontre o professor particular perfeito
lista.add(123); // Compila, mas lança ClassCastException ao recuperar
Com Generics, o tipo é definido na declaração, eliminando a necessidade de casts explícitos e aumentando a segurança:
Com Generics:
List<String> lista = new ArrayList<>();
lista.add("Texto");
String s = lista.get(0); // Sem casting
Como Funciona o Generics
Os Generics funcionam parametrizando o tipo em tempo de compilação. Isso significa que você pode especificar o tipo ao criar uma classe, interface ou método genérico.
Exemplo de Classe Genérica
public class Caixa<T> {
private T objeto;
public void set(T objeto) {
this.objeto = objeto;
}
public T get() {
return objeto;
}
}
public class Main {
public static void main(String[] args) {
Caixa<String> caixa = new Caixa<>();
caixa.set("Olá, Generics!");
System.out.println(caixa.get()); // Saída: Olá, Generics!
}
}
Interface Genérica
public interface Comparador<T> {
int comparar(T o1, T o2);
}
public class ComparadorString implements Comparador<String> {
@Override
public int comparar(String o1, String o2) {
return o1.compareTo(o2);
}
}
Métodos Genéricos
Você também pode criar métodos genéricos que funcionam com diferentes tipos, sem precisar parametrizar a classe inteira.
Exemplo:
public class Util {
public static <T> void imprimirArray(T[] array) {
for (T elemento : array) {
System.out.println(elemento);
}
}
public static void main(String[] args) {
Integer[] numeros = {1, 2, 3};
String[] palavras = {"A", "B", "C"};
Util.imprimirArray(numeros);
Util.imprimirArray(palavras);
}
}
Wildcards (Curingas)
O wildcard (?
) é usado quando você não sabe ou não precisa especificar o tipo exato em um parâmetro genérico.
Tipos de Wildcards
-
Unbounded Wildcard (
?
): Aceita qualquer tipo.public void imprimirLista(List<?> lista) { for (Object elemento : lista) { System.out.println(elemento); } }
-
Upper Bounded Wildcard (
<? extends T>
): Permite tipos que são subtipos deT
.public void imprimirNumeros(List<? extends Number> numeros) { for (Number numero : numeros) { System.out.println(numero); } }
-
Lower Bounded Wildcard (
<? super T>
): Permite tipos que são supertipos deT
.public void adicionarElemento(List<? super Integer> lista) { lista.add(123); }
Restrições e Limitações
-
Tipos Primitivos: Generics não funcionam diretamente com tipos primitivos como
int
oudouble
. Use as classes wrapper (Integer
,Double
) em vez disso.List<Integer> lista = new ArrayList<>();
-
Erasure (Apagamento de Tipos): Durante a compilação, os tipos genéricos são apagados e substituídos por
Object
. Isso significa que:- Não é possível instanciar arrays de tipos genéricos.
- Não há sobrecarga de métodos com base em tipos genéricos.
Benefícios dos Generics
- Segurança de Tipo: Reduz erros de
ClassCastException
em tempo de execução. - Reutilização de Código: Permite criar classes e métodos que funcionam para diferentes tipos.
- Legibilidade: Torna o código mais claro, especificando explicitamente os tipos esperados.
Os Generics tornam o Java mais seguro e flexível, permitindo que desenvolvedores criem código reutilizável e menos propenso a erros. Ao entender como funcionam e explorar seus recursos, você pode melhorar a qualidade e a robustez de seus projetos. Experimente implementar Generics em suas classes e métodos para descobrir o quanto eles podem simplificar e aprimorar seu código!