Menu
Coddy logo textTech

Introdução a Generics

Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy — lição 53 de 87.

Imagine que você precise criar uma classe que armazena um valor e o recupera mais tarde. Sem generics, você teria que escrever classes separadas para cada tipo — uma para Integer, uma para String, uma para Double, e assim por diante. Isso leva à duplicação de código e dores de cabeça na manutenção.

Generics resolvem esse problema permitindo que você escreva classes e métodos que funcionam com qualquer tipo, mantendo a segurança de tipos. Você define um marcador (chamado de parâmetro de tipo) que é substituído por um tipo real quando você usa a classe:

// Sem generics - usa Object, requer casting
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0);  // Casting manual necessário

// Com generics - type-safe, sem casting
List<String> list = new ArrayList<String>();
list.add("Hello");
String s = list.get(0);  // Sem necessidade de casting

Os colchetes angulares <T> denotam um parâmetro de tipo. Quando você escreve List<String>, você está dizendo ao compilador que esta lista conterá apenas strings. Se você tentar adicionar um número inteiro, o compilador captura o erro imediatamente, em vez de falhar em tempo de execução.

Generics oferecem dois benefícios principais: eles eliminam a necessidade de casting explícito e detectam erros de tipo em tempo de compilação, em vez de tempo de execução. Isso torna seu código mais seguro e limpo. Nas lições a seguir, exploraremos como criar suas próprias classes e métodos genéricos.

challenge icon

Desafio

Fácil

Vamos construir um sistema de armazenamento simples que demonstra o poder dos generics comparando a maneira antiga (usando Object com casting) versus a nova maneira (usando generics com segurança de tipos).

Você criará três arquivos para ver a diferença em primeira mão:

  • OldBox.java: Crie uma classe que armazena valores da maneira anterior aos generics. Sua OldBox deve ter um campo privado content do tipo Object. Inclua um construtor que aceite um Object e o armazene, e um método getContent() que retorne o Object. Essa abordagem funciona, mas requer casting ao recuperar os valores.
  • GenericBox.java: Agora crie a versão moderna e segura quanto ao tipo usando generics. Sua GenericBox deve usar um parâmetro de tipo T em sua declaração. Ela precisa de um campo privado content do tipo T, um construtor que aceite um valor T e um método getContent() que retorne o tipo T. Nenhum casting é necessário ao usar esta classe!
  • Main.java: Demonstre ambas as abordagens lado a lado. Você receberá duas entradas: uma palavra (String) e um número (inteiro).

    Primeiro, use a abordagem antiga: crie uma OldBox armazenando a palavra, depois recupere-a com um cast para String e imprima: OldBox (with cast): [value]

    Em seguida, use a abordagem genérica: crie um GenericBox<String> armazenando a mesma palavra, recupere-a sem casting e imprima: GenericBox (no cast): [value]

    Depois, mostre os generics funcionando com inteiros: crie um GenericBox<Integer> armazenando o número, recupere-o e imprima: GenericBox Integer: [value]

    Por fim, imprima uma linha em branco seguida por: Type safety: Generics catch errors at compile time!

Você receberá duas entradas em ordem: uma palavra (String) e um número (inteiro como String, que você deverá converter).

Observe a diferença fundamental: com OldBox, você deve fazer o cast (String) box.getContent() e torcer para ter acertado o tipo. Com GenericBox, o compilador conhece o tipo e nenhum casting é necessário. Este é o benefício central dos generics — segurança de tipos sem o incômodo do casting!

Folha de consulta

Generics permitem que você escreva classes e métodos que funcionam com qualquer tipo, mantendo a segurança de tipos (type safety). Eles usam parâmetros de tipo (placeholders) que são substituídos pelos tipos reais quando a classe é utilizada.

Sem generics (usando Object):

List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0);  // Manual casting required

Com generics (type-safe):

List<String> list = new ArrayList<String>();
list.add("Hello");
String s = list.get(0);  // No casting needed

Criando uma classe genérica:

public class GenericBox<T> {
    private T content;
    
    public GenericBox(T content) {
        this.content = content;
    }
    
    public T getContent() {
        return content;
    }
}

Usando uma classe genérica:

GenericBox<String> stringBox = new GenericBox<String>("Hello");
String value = stringBox.getContent();  // No casting

GenericBox<Integer> intBox = new GenericBox<Integer>(42);
Integer number = intBox.getContent();  // Type-safe

Os colchetes angulares <T> denotam um parâmetro de tipo. Os benefícios incluem: nenhum casting explícito é necessário e os erros de tipo são capturados em tempo de compilação em vez de tempo de execução.

Experimente você mesmo

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String word = scanner.nextLine();
        int number = Integer.parseInt(scanner.nextLine());
        
        // TODO: Criar um OldBox armazenando a word
        // Recuperar com cast para String e imprimir: "OldBox (with cast): [value]"
        
        // TODO: Criar um GenericBox<String> armazenando a word
        // Recuperar sem casting e imprimir: "GenericBox (no cast): [value]"
        
        // TODO: Criar um GenericBox<Integer> armazenando o number
        // Recuperar e imprimir: "GenericBox Integer: [value]"
        
        // TODO: Imprimir uma linha em branco, depois imprimir:
        // "Type safety: Generics catch errors at compile time!"
    }
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos