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 recursos principais da linguagem Java, introduzido na versão J2SE 5.0. Eles permitem criar classes, interfaces e métodos que trabalham com diferentes tipos de dados, mantendo a tipagem estrita em tempo de compilação. Neste artigo, vamos entender para que servem os genéricos, como usá-los corretamente e quais erros eles ajudam a evitar.



Para que servem os Generics em Java?



Antes do surgimento dos genéricos, os programadores usavam casting (conversão de tipos) e trabalhavam com Object. Isso gerava dois problemas principais:



  • Falta de type safety — qualquer tipo de objeto podia ser adicionado a uma coleção, e o erro só aparecia em tempo de execução.
  • Necessidade de casting explícito — ao recuperar um elemento da coleção, era necessário fazer um cast, o que poluía o código.


// Sem Generics (Java anterior ao 5)List list = new ArrayList();list.add("string");list.add(123); // Erro não ocorre em tempo de compilaçãoString s = (String) list.get(0); // É necessário fazer cast explícito


// Com GenericsList<String> list = new ArrayList<>();list.add("string");// list.add(123); // Erro de compilação!String s = list.get(0); // Cast não é necessário


Principais vantagens dos Generics:

  • Type safety — erros de tipo são detectados em tempo de compilação.
  • Eliminação de casts explícitos — o código fica mais limpo e legível.
  • Reutilização de código — uma única implementação funciona com diferentes tipos.


Criação de classes e métodos genéricos



Classe genérica



Para criar uma classe que trabalha com um tipo arbitrário, basta especificar o parâmetro de tipo entre colchetes angulares após o nome da classe:



public class Box<T> {    private T content;

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

public T getContent() { return content; }

public static void main(String[] args) { Box<String> stringBox = new Box<>(); stringBox.setContent("Olá, Java!"); System.out.println(stringBox.getContent());

Box<Integer> intBox = new Box<>(); intBox.setContent(42); System.out.println(intBox.getContent()); }}


Neste exemplo, T é o parâmetro de tipo. Ao criar o objeto Box<String>, todas as ocorrências de T são substituídas por String.



Método genérico



Métodos também podem ser parametrizados. O parâmetro de tipo é especificado antes do tipo de retorno:



public class Util {    public static <T> void printArray(T[] array) {        for (T element : array) {            System.out.print(element + " ");        }        System.out.println();    }

public static void main(String[] args) { Integer[] intArray = {1, 2, 3, 4, 5}; String[] strArray = {"A", "B", "C"};

printArray(intArray); // 1 2 3 4 5 printArray(strArray); // A B C }}


Observe: o parâmetro de tipo <T> vem antes de void. Isso é obrigatório para métodos genéricos.



Tipos limitados (Bounded Type Parameters)



Às vezes é necessário restringir quais tipos podem ser usados como parâmetro. Para isso, utilizam-se as palavras-chave extends e super.



Limite superior (extends)



Restringe o tipo por cima — permite usar apenas a própria classe especificada ou suas subclasses:



public class Calculator<T extends Number> {    private T value;

public Calculator(T value) { this.value = value; }

public double square() { return va

Blogs

Book Recommendations