Classes Genéricas
Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy — lição 54 de 87.
Agora que você entende por que os genéricos são úteis, vamos criar sua própria classe genérica. Uma classe genérica declara um ou mais parâmetros de tipo entre colchetes angulares após o nome da classe, que podem então ser usados em toda a classe.
class Box<T> {
private T content;
public void set(T content) {
this.content = content;
}
public T get() {
return content;
}
}O T é um parâmetro de tipo — um marcador de posição que é substituído por um tipo real quando você cria uma instância. Você pode usar qualquer nome, mas letras maiúsculas únicas são convencionais: T para Type, E para Element, K para Key, V para Value.
Ao instanciar uma classe genérica, você especifica o tipo real entre colchetes angulares:
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String value = stringBox.get(); // Nenhum casting necessário
Box<Integer> intBox = new Box<>();
intBox.set(42);
Integer num = intBox.get();Observe o <> (operador diamante) no lado direito — o Java infere o tipo a partir da declaração. Você também pode usar múltiplos parâmetros de tipo para classes que precisam trabalhar com mais de um tipo:
class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
Pair<String, Integer> pair = new Pair<>("age", 25);Desafio
FácilVamos construir um sistema de registro de presentes que demonstra o poder das classes genéricas. Você criará um sistema de armazenamento flexível que pode conter qualquer tipo de item de presente, além de um registro que associa nomes de presentes com seus detalhes usando múltiplos parâmetros de tipo.
Você organizará seu código em três arquivos:
GiftBox.java: Crie uma classe genérica que possa embrulhar qualquer tipo de presente. SuaGiftBox<T>deve ter um campo privadoitemdo tipoTe um campo privadowrapped(boolean) que começa comofalse. Inclua um construtor que aceite o item, um métodogetItem()que retorne o item, um métodowrap()que defina wrapped como true, e um métodoisWrapped()que retorne o status de embrulhado. Adicione um métodogetStatus()que retorne[item] (wrapped)ou[item] (unwrapped)dependendo do estado de embrulho.Registry.java: Crie uma classe genérica com dois parâmetros de tipo que associa um destinatário ao seu presente. SuaRegistry<K, V>deve ter campos privadosrecipientdo tipoKegiftdo tipoV. Inclua um construtor que aceite ambos os valores, métodos gettergetRecipient()egetGift(), e um métodogetEntry()que retorne:[recipient] -> [gift]Main.java: Dê vida ao seu registro de presentes! Você receberá três entradas: um nome de destinatário (String), uma descrição de presente (String) e um valor de presente (inteiro representando dólares).Primeiro, crie uma
GiftBox<String>contendo a descrição do presente. Embrulhe-a usando o métodowrap()e, em seguida, imprima seu status.Em seguida, crie uma
GiftBox<Integer>contendo o valor do presente (representando o valor de um cartão-presente). Não embrulhe esta — imprima seu status para mostrar que está desembrulhada.Finalmente, crie uma
Registry<String, String>associando o nome do destinatário com a descrição do presente e imprima a entrada do registro.
Você receberá três entradas em ordem: nome do destinatário, descrição do presente e valor do presente (como um número inteiro).
Observe como a mesma classe GiftBox funciona perfeitamente com os tipos String e Integer — essa é a beleza dos genéricos! E a classe Registry demonstra como múltiplos parâmetros de tipo permitem criar associações flexíveis entre quaisquer dois tipos.
Folha de consulta
Uma classe genérica declara um ou mais parâmetros de tipo entre colchetes angulares após o nome da classe, que podem ser usados em toda a classe:
class Box<T> {
private T content;
public void set(T content) {
this.content = content;
}
public T get() {
return content;
}
}O T é um parâmetro de tipo — um marcador de posição substituído por um tipo real ao criar uma instância. Convenções comuns usam letras maiúsculas únicas: T para Type (Tipo), E para Element (Elemento), K para Key (Chave), V para Value (Valor).
Ao instanciar uma classe genérica, especifique o tipo real entre colchetes angulares:
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String value = stringBox.get(); // Nenhuma conversão necessária
Box<Integer> intBox = new Box<>();
intBox.set(42);
Integer num = intBox.get();O <> (operador diamante) permite que o Java infira o tipo a partir da declaração.
Use múltiplos parâmetros de tipo para classes que trabalham com mais de um tipo:
class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
Pair<String, Integer> pair = new Pair<>("age", 25);Experimente você mesmo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Ler as entradas
String recipientName = scanner.nextLine();
String giftDescription = scanner.nextLine();
int giftValue = scanner.nextInt();
// TODO: Criar uma GiftBox<String> contendo a gift description
// TODO: Embrulhá-la usando o método wrap()
// TODO: Imprimir seu status usando getStatus()
// TODO: Criar uma GiftBox<Integer> contendo o gift value
// TODO: Não embrulhe esta - imprima seu status (deve mostrar unwrapped)
// TODO: Criar um Registry<String, String> pareando recipient com gift description
// TODO: Imprimir a entrada do registro usando getEntry()
}
}
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 Iterator