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 tipoOs 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.
Desafio
FácilVamos 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 privadocontentdo tipoObject. Inclua um construtor que aceite um Object e o armazene, e um métodogetContent()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 tipoTem sua declaração. Ela precisa de um campo privadocontentdo tipoT, um construtor que aceite um valorTe um métodogetContent()que retorne o tipoT. 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!"
}
}
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
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosA Palavra-chave thisMétodosCampos (Atributos)Método ConstrutorSobrecarga de ConstrutorRecapitulação - Calculadora Simples4Herança
Herança Básica (extends)A Palavra-chave superSobrescrita de Método (@Override)Encadeamento de ConstrutoresA Classe ObjectHerança Simples e MultinívelPor que não há Herança Múltipla de ClassesRecapitulação - Hierarquia de Funcionários7Métodos Especiais e a Classe Object
Método toString()equals() e hashCode()Método clone()compareTo() e ComparableInterface ComparatorRecapitulação - Ordenação Personalizada2Modificadores de Acesso e Encapsulamento
Visão Geral dos Níveis de AcessoMétodos Getter e SetterOcultação de InformaçõesA Palavra-chave finalRecapitulação - Gerenciador de Conta Bancária5Polimorfismo
Fundamentos de Sobrecarga de MétodosSobrescrita de Métodos (Tempo de Execução)Upcasting e DowncastingO Operador instanceofClasses e Métodos AbstratosRecapitulação - Calculadora de Formas8Conceitos Avançados de OOP
Composição vs HerançaAgregação vs ComposiçãoClasses Internas, Aninhadas e AnônimasEnums e Métodos de EnumRecords (Java 16+)Classes Seladas (Java 17+)11Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão BuilderPadrão ObserverPadrão Strategy3Propriedades de Classe e Membros Estáticos
Variáveis de Instância vs EstáticasMétodos EstáticosBlocos EstáticosConstantes (static final)Recapitulação - Contador e Utilitário6Interfaces e Classes Abstratas
Introdução às InterfacesImplementando InterfacesImplementação de Múltiplas InterfacesDefault e Static em InterfacesClasses Abstratas vs InterfacesInterfaces FuncionaisRecapitulação - Sistema de Pagamento9Generics
Introdução a GenericsClasses GenéricasMétodos GenéricosParâmetros de Tipos DelimitadosWildcards (?, extends, super)Recapitulação - Container Genérico12Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão IteratorPratique por conta própria: Compilador de Java online