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 armazene um valor e o recupere posteriormente. 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.

Genéricos resolvem esse problema permitindo que você escreva classes e métodos que funcionem 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, requer casting
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0);  // Casting manual necessário

// Com genéricos - type-safe, sem casting
List<String> list = new ArrayList<String>();
list.add("Hello");
String s = list.get(0);  // Nenhum casting necessário

Os sinais de menor e maior <T> denotam um parâmetro de tipo. Quando você escreve List<String>, está informando ao compilador que essa lista conterá apenas strings. Se você tentar adicionar um inteiro, o compilador detectará o erro imediatamente, em vez de falhar em tempo de execução.

Os genéricos oferecem dois benefícios principais: eliminam a necessidade de conversão explícita de tipos 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 mais limpo. Nas lições a seguir, exploraremos como criar suas próprias classes e métodos genéricos.

challenge icon

Desafio

Fácil

Vamos criar um sistema de armazenamento simples que demonstra o poder dos genéricos, comparando a forma 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 forma anterior aos genéricos. Sua OldBox deve ter um campo privado content do tipo Object. Inclua um construtor que aceite um Object e o armazene, além de um método getContent() que retorne o Object. Essa abordagem funciona, mas exige conversão de tipo ao recuperar os valores.
  • GenericBox.java: agora crie a versão moderna e segura em relação 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, de um construtor que aceite um valor T e de um método getContent() que retorne o tipo T. Nenhuma conversão de tipo é necessária 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 armazenando a palavra, depois recupere-a com uma conversão para String e imprima: OldBox (with cast): [value]

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

    Depois, mostre os genéricos funcionando com inteiros: crie uma GenericBox<Integer> armazenando 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ê deverá analisar).

Observe a principal diferença: com OldBox, você precisa fazer a conversão (String) box.getContent() e torcer para ter obtido o tipo correto. Com GenericBox, o compilador conhece o tipo e nenhuma conversão é necessária. Esse é o principal benefício dos genéricos: segurança de tipos sem o trabalho adicional 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 um OldBox armazenando a word
        // Retrieve with cast to String and print: "OldBox (with cast): [value]"
        
        // TODO: Crie um GenericBox<String> armazenando a word
        // Retrieve without casting and print: "GenericBox (no cast): [value]"
        
        // TODO: Create a GenericBox<Integer> storing the number
        // Retrieve and print: "GenericBox Integer: [value]"
        
        // TODO: Imprima uma linha em branco, 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