Métodos Genéricos
Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy — lição 55 de 87.
Enquanto classes genéricas fazem com que classes inteiras funcionem com diferentes tipos, às vezes você só precisa que um único método seja genérico. Um método genérico declara seu próprio parâmetro de tipo, independentemente de quaisquer genéricos de nível de classe.
O parâmetro de tipo aparece antes do tipo de retorno na assinatura do método:
public class Utility {
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
}
String[] names = {"Alice", "Bob"};
Integer[] numbers = {1, 2, 3};
Utility.printArray(names); // Funciona com String[]
Utility.printArray(numbers); // Funciona com Integer[]Observe o <T> colocado entre os modificadores e o tipo de retorno. Isso declara T como um parâmetro de tipo para este método específico. O compilador infere o tipo real a partir dos argumentos que você passa.
Métodos genéricos também podem retornar o tipo genérico:
public static <T> T getFirst(T[] array) {
if (array.length > 0) {
return array[0];
}
return null;
}
String first = getFirst(new String[]{"a", "b"}); // Retorna "a"Você pode usar múltiplos parâmetros de tipo quando necessário:
public static <K, V> void printPair(K key, V value) {
System.out.println(key + ": " + value);
}
printPair("Age", 25); // String e Integer
printPair(1, "First"); // Integer e StringMétodos genéricos são particularmente úteis em classes utilitárias onde você precisa de operações flexíveis e reutilizáveis sem criar uma classe genérica.
Desafio
FácilVamos construir um kit de ferramentas utilitárias que demonstra a flexibilidade dos métodos genéricos. Você criará uma classe com métodos reutilizáveis que funcionam com qualquer tipo, demonstrando como métodos genéricos podem existir independentemente de classes genéricas.
Você organizará seu código em dois arquivos:
ArrayUtils.java: Crie uma classe utilitária contendo métodos genéricos para trabalhar com arrays. Sua classe deve ter três métodos genéricos estáticos:getLast- Um método genérico que recebe um array do tipoTe retorna o último elemento. Se o array estiver vazio, retornenull.swap- Um método genérico que recebe um array do tipoTe dois índices inteiros, e então troca os elementos nessas posições. Este método não retorna nada.printWithLabel- Um método genérico com dois parâmetros de tipoKeV. Ele recebe um rótulo do tipoKe um valor do tipoV, e então os imprime no formato:[label]: [value]Main.java: Coloque seus métodos genéricos para funcionar! Você receberá quatro entradas: duas palavras (Strings) e dois números (inteiros).Primeiro, crie um array
String[]contendo ambas as palavras. UsegetLastpara obter o último elemento e imprima:Last word: [result]Em seguida, crie um array
Integer[]contendo ambos os números. Chameswappara trocar os elementos nos índices 0 e 1, e então imprima:After swap: [first], [second]Finalmente, demonstre o método genérico de dois parâmetros chamando
printWithLabelduas vezes:- Uma vez com a primeira palavra como o rótulo e o primeiro número como o valor
- Uma vez com o primeiro número como o rótulo e a segunda palavra como o valor
Você receberá quatro entradas na ordem: primeira palavra, segunda palavra, primeiro número, segundo número.
Observe como cada método declara seu(s) próprio(s) parâmetro(s) de tipo com <T> ou <K, V> antes do tipo de retorno. O compilador infere os tipos reais a partir dos argumentos que você passa — não há necessidade de especificá-los explicitamente ao chamar os métodos!
Folha de consulta
Um método genérico declara seu próprio parâmetro de tipo, independente de quaisquer genéricos de nível de classe. O parâmetro de tipo aparece antes do tipo de retorno na assinatura do método.
Sintaxe básica de método genérico:
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}O <T> é colocado entre os modificadores e o tipo de retorno. O compilador infere o tipo real a partir dos argumentos que você passa.
Métodos genéricos podem retornar o tipo genérico:
public static <T> T getFirst(T[] array) {
if (array.length > 0) {
return array[0];
}
return null;
}Múltiplos parâmetros de tipo podem ser usados:
public static <K, V> void printPair(K key, V value) {
System.out.println(key + ": " + value);
}
printPair("Age", 25); // String and Integer
printPair(1, "First"); // Integer and StringMétodos genéricos são úteis em classes utilitárias para operações flexíveis e reutilizáveis sem a necessidade de criar uma classe genérica.
Experimente você mesmo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Ler entradas
String firstWord = scanner.nextLine();
String secondWord = scanner.nextLine();
int firstNumber = scanner.nextInt();
int secondNumber = scanner.nextInt();
// TODO: Criar um array String[] contendo ambas as palavras
// TODO: Usar ArrayUtils.getLast para obter o último elemento
// Imprimir: "Last word: [result]"
// TODO: Criar um array Integer[] contendo ambos os números
// TODO: Chamar ArrayUtils.swap para trocar elementos nos índices 0 e 1
// Imprimir: "After swap: [first], [second]"
// TODO: Chamar ArrayUtils.printWithLabel com firstWord como rótulo e firstNumber como valor
// TODO: Chamar ArrayUtils.printWithLabel com firstNumber como rótulo e secondWord como valor
}
}
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