Menu
CoddyTech

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ê precisa criar uma classe que armazene um valor e o recupere mais tarde. Sem genéricos, 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 a dificuldades de manutenção.

Tipos genéricos 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 espaço reservado (chamado de parâmetro de tipo) que é substituído por um tipo real quando você usa a classe:

// Sem genéricos - usa Object, exige conversão de tipo
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0);  // Conversão manual de tipo necessária

// Com genéricos - seguro quanto a tipos, sem conversão de tipo
List<String> list = new ArrayList<String>();
list.add("Hello");
String s = list.get(0);  // Não é necessária conversão de tipo

Os sinais de menor e maior <T> indicam um parâmetro de tipo. Quando você escreve List<String>, está informando ao compilador que essa lista conterá apenas valores do tipo string. Se você tentar adicionar um número inteiro, o compilador detectará o erro imediatamente, em vez de falhar durante a execução.

Os genéricos oferecem dois benefícios principais: eliminam a necessidade de conversões de tipo explícitas e detectam erros de tipo em tempo de compilação, em vez de em tempo de execução. Isso torna seu código mais seguro e organizado. Nas próximas lições, vamos explorar como criar suas próprias classes e métodos genéricos.

challenge icon

Desafio

Fácil

Vamos criar um sistema simples de armazenamento que demonstra o poder dos genéricos ao comparar a maneira antiga (usando Object com conversão de tipo) com a nova (usando genéricos com segurança de tipos).

Você criará três arquivos para ver a diferença na prática:

  • OldBox.java: Crie uma classe que armazene valores da maneira usada antes dos genéricos. 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 exige conversão de tipo ao recuperar valores.
  • GenericBox.java: Agora, crie a versão moderna e segura quanto aos tipos, usando genéricos. 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. Não é necessária conversão de tipo ao usar essa classe!
  • Main.java: Demonstre as duas abordagens lado a lado. Você receberá duas entradas: uma palavra (String) e um número (inteiro).

    Primeiro, use a abordagem antiga: crie uma OldBox que armazene a palavra, depois recupere-a convertendo o tipo para String e imprima: OldBox (with cast): [value]

    Em seguida, use a abordagem genérica: crie uma GenericBox<String> que armazene a mesma palavra, recupere-a sem converter o tipo e imprima: GenericBox (no cast): [value]

    Depois, mostre os genéricos funcionando com números inteiros: crie uma GenericBox<Integer> que armazene o número, recupere-o e imprima: GenericBox Integer: [value]

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

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

Observe a principal diferença: com OldBox, você precisa converter o tipo usando (String) box.getContent() e torcer para que o tipo esteja correto. Com GenericBox, o compilador conhece o tipo e não é necessária nenhuma conversão. Esse é o principal benefício dos genéricos: segurança de tipos sem o incômodo das conversões!

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: Crie uma OldBox que armazene a palavra
        // Recupere com conversão para String e imprima: "OldBox (with cast): [value]"
        
        // TODO: Crie uma GenericBox<String> que armazene a palavra
        // Recupere sem conversão e imprima: "GenericBox (no cast): [value]"
        
        // TODO: Crie uma GenericBox<Integer> que armazene o número
        // Recupere e imprima: "GenericBox Integer: [value]"
        
        // TODO: Imprima uma linha em branco e depois imprima:
        // "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

Pratique por conta própria: Compilador de Java online