Wildcards (?, extends, super)
Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy. Lição 57 de 87.
Os parâmetros de tipo limitados restringem os tipos genéricos a tipos específicos, mas e quando você está trabalhando com tipos genéricos existentes e não sabe, ou não se importa com, o parâmetro de tipo exato? É aqui que entram os curingas.
O curinga não limitado ? representa um tipo desconhecido. Ele é útil quando você deseja aceitar qualquer tipo parametrizado:
public static void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}
printList(new ArrayList<String>()); // Funciona
printList(new ArrayList<Integer>()); // FuncionaO curinga com limite superior ? extends Type aceita o tipo especificado ou qualquer uma de suas subclasses. Use-o quando precisar ler de uma estrutura genérica:
public static double sumNumbers(List<? extends Number> numbers) {
double sum = 0;
for (Number n : numbers) {
sum += n.doubleValue();
}
return sum;
}
sumNumbers(Arrays.asList(1, 2, 3)); // Lista de Integer
sumNumbers(Arrays.asList(1.5, 2.5)); // Lista de DoubleO curinga com limite inferior ? super Type aceita o tipo especificado ou qualquer uma de suas superclasses. Use-o quando precisar escrever em uma estrutura genérica:
public static void addIntegers(List<? super Integer> list) {
list.add(1);
list.add(2);
}
List<Number> numbers = new ArrayList<>();
addIntegers(numbers); // Funciona - Number é um supertipo de IntegerUma orientação útil: use extends quando você apenas lê (produtor) e use super quando você apenas escreve (consumidor). Isso é conhecido como o princípio PECS: Producer Extends, Consumer Super.
Desafio
FácilVamos criar um sistema de alimentação de zoológico que demonstra os três tipos de curingas! Você criará uma hierarquia de animais e alimentos e, em seguida, escreverá métodos utilitários que usam curingas sem limite, com limite superior e com limite inferior para lidar com diferentes cenários de alimentação.
Você organizará seu código em quatro arquivos:
Food.java: Crie uma hierarquia de classes simples para alimentos. Comece com uma classe baseFoodque tenha um campo privadoname(String), um construtor para inicializá-lo e um métodogetName(). Em seguida, crie duas subclasses no mesmo arquivo:Meatestende Food (o construtor chamasuper(name)) eVegetableestende Food (seguindo o mesmo padrão).Animal.java: Crie uma classe baseAnimalcom um campo privadospecies(String), um construtor e um métodogetSpecies(). Crie duas subclasses:CarnivoreeHerbivore, cada uma estendendo Animal e chamando o construtor da classe pai.ZooFeeder.java: É aqui que os curingas mostram seu poder! Crie uma classe utilitária com três métodos estáticos:printInventory(List<?> items)— Usa um curinga sem limite para imprimir qualquer lista. Percorra a lista e imprima cada item usandotoString(), um por linha.calculateTotalFood(List<? extends Food> foods)— Usa um curinga com limite superior para ler uma lista de qualquer tipo de Food. Retorne a quantidade de itens como umint. Isso demonstra a leitura de um produtor.addMeatToStock(List<? super Meat> stock, String meatName)— Usa um curinga com limite inferior para escrever em uma lista que pode armazenar Meat. Crie um novo objetoMeatcom o nome fornecido e adicione-o ao estoque. Isso demonstra a escrita em um consumidor.Main.java: Reúna seu sistema de alimentação do zoológico! Você receberá duas entradas: um nome de carne (String) e um nome de vegetal (String).Primeiro, crie um
ArrayList<Food>e adicione um objetoMeatcom o nome da carne e um objetoVegetablecom o nome do vegetal. ImprimaFood inventory:e, em seguida, chameprintInventorycom essa lista.Depois, imprima uma linha em branco e, em seguida, imprima
Total food items: [count]usandocalculateTotalFoodna sua lista de alimentos.Por fim, crie um novo
ArrayList<Food>chamadomeatStock. Imprima uma linha em branco e, em seguida,Adding to meat stock.... ChameaddMeatToStockcom essa lista e a string"Beef". Depois, imprimaMeat stock after adding:e chameprintInventoryno estoque de carne.
Você receberá duas entradas, nesta ordem: um nome de carne e um nome de vegetal.
Substitua toString() na sua classe Food para retornar o nome do alimento, para que a impressão funcione corretamente. Observe como cada tipo de curinga serve a uma finalidade diferente: ? para obter flexibilidade máxima quando você só precisa ler como Object, ? extends ao ler tipos específicos e ? super ao escrever em uma coleção!
Experimente você mesmo
import java.util.Scanner;
import java.util.ArrayList;
import java.util.List;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String meatName = scanner.nextLine();
String vegetableName = scanner.nextLine();
// TODO: Crie um ArrayList<Food> e adicione objetos Meat e Vegetable
// TODO: Print "Food inventory:" then call printInventory
// TODO: Print blank line, then "Total food items: [count]" using calculateTotalFood
// TODO: Crie um novo ArrayList<Food> chamado meatStock
// TODO: Print blank line, then "Adding to meat stock..."
// TODO: Chame addMeatToStock com meatStock e "Beef"
// TODO: Print "Meat stock after adding:" then call printInventory on meatStock
}
}
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