Generics em Java: para que servem e como usar

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Generics em Java: para que servem e como usar



Generics (genéricos) — um dos principais recursos do Java, introduzido na versão J2SE 5.0. Eles permitem criar classes, interfaces e métodos que trabalham com tipos de dados especificados no momento da chamada. Em vez de escrever implementações separadas para Integer, String e Double, você escreve um único código universal. Neste artigo, vamos entender para que servem os Generics em Java, como usá-los na prática e quais armadilhas você deve conhecer.



Para que servem os Generics em Java?



Antes do surgimento dos Generics, os desenvolvedores usavam Object para armazenar qualquer tipo. Isso funcionava, mas gerava dois problemas sérios:



  • Falta de verificação de tipos em tempo de compilação. Você podia acidentalmente colocar uma String em uma lista no lugar de Integer, e o erro só aparecia durante a execução do programa.
  • Necessidade de conversão explícita de tipos. Cada vez que um elemento era extraído de uma coleção, era necessário um cast: (String) list.get(0). Isso tornava o código verboso e perigoso.


Os Generics resolvem ambos os problemas. Eles permitem que o compilador verifique a correção dos tipos e realize a conversão automaticamente. Vejamos um exemplo simples:



// Sem Generics (estilo antigo)List list = new ArrayList();list.add("Hello");String s = (String) list.get(0); // conversão explícita necessária

// Com GenericsList<String> list = new ArrayList<>();list.add("Hello");String s = list.get(0); // conversão não necessária


O código ficou mais seguro e legível. Erros de tipo são detectados em tempo de compilação, não em tempo de execução.



Como usar Generics: construções principais



1. Classes Genéricas



Você pode definir uma classe que aceita um ou mais parâmetros de tipo. Geralmente, usa-se uma letra: T (Type), E (Element), K (Key), V (Value).



public class Box<T> {    private T content;

public void put(T item) { this.content = item; }

public T get() { return content; }}

// UsoBox<String> stringBox = new Box<>();stringBox.put("Java");String value = stringBox.get(); // conversão automática


O parâmetro T é substituído pelo tipo real ao criar o objeto. O compilador garante que não é possível colocar um Integer em um Box<String>.



2. Métodos Genéricos



Métodos também podem ser genéricos, mesmo que a classe não seja. O parâmetro de tipo é especificado antes do tipo de retorno:



public class Utils {    public static <T> T getMiddle(T... args) {        return args[args.length / 2];    }}

// ChamadaString middle = Utils.<String>getMiddle("A", "B", "C");// ou simplesmente: Utils.getMiddle("A", "B", "C") — o compilador infere o tipo


Métodos genéricos são úteis para funções utilitárias que trabalham com diferentes tipos de dados.



3. Tipos Limitados (Bounded Type Parameters)



Às vezes, é necessário restringir quais tipos podem ser usados. Por exemplo, para que um método funcione apenas com números:



public class Calculator {    public static <T extends Number> double sum(T a, T b) {        return a.doubleValue() + b.doubleValue();    }}

// Funciona com Integer, Double, Float, etc.double result = Calculator.sum(5, 10); // 15.0


A palavra-chave extends significa que T deve ser um subtipo da classe especificada (ou implementar a interface).



Wildcards: tipos desconhecidos



Wildcards (curingas) são usados quando o tipo exato é desconhecido ou irrelevante. São representados pelo ponto de interrogação ?. Existem três tipos: