Wildcards (?, extends, super)
Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy — lição 57 de 87.
Parâmetros de tipo delimitados restringem 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 wildcards.
O wildcard ilimitado ? representa um tipo desconhecido. É ú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 wildcard de 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 wildcard de limite inferior (lower bounded wildcard) ? 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 diretriz útil: use extends quando você apenas lê (produtor), use super quando você apenas escreve (consumidor). Isso é conhecido como o princípio PECS — Producer Extends, Consumer Super.
Desafio
FácilVamos construir um sistema de alimentação de zoológico que demonstra todos os três tipos de wildcards! Você criará uma hierarquia de animais e alimentos e, em seguida, escreverá métodos utilitários que usam wildcards ilimitados (unbounded), com limite superior (upper bounded) e com limite inferior (lower bounded) 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 possui 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 (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 pai.ZooFeeder.java: É aqui que os wildcards brilham! Crie uma classe utilitária com três métodos estáticos:printInventory(List<?> items)- Usa um wildcard ilimitado para imprimir qualquer lista. Percorra e imprima cada item usandotoString(), um por linha.calculateTotalFood(List<? extends Food> foods)- Usa um wildcard com limite superior para ler de uma lista de qualquer tipo de Food. Retorne a contagem de itens como umint. Isso demonstra a leitura de um produtor.addMeatToStock(List<? super Meat> stock, String meatName)- Usa um wildcard com limite inferior para escrever em uma lista que pode conter 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 chameprintInventorycom esta lista.Em seguida, imprima uma linha em branco e, depois, imprima
Total food items: [count]usandocalculateTotalFoodem sua lista de alimentos.Finalmente, crie um novo
ArrayList<Food>chamadomeatStock. Imprima uma linha em branco e, depois,Adding to meat stock.... ChameaddMeatToStockcom esta lista e a string"Beef". Em seguida, imprimaMeat stock after adding:e chameprintInventoryno estoque de carne.
Você receberá duas entradas em ordem: um nome de carne e um nome de vegetal.
Sobrescreva toString() em sua classe Food para retornar o nome do alimento, para que a impressão funcione corretamente. Observe como cada tipo de wildcard serve a um propósito diferente: ? para flexibilidade máxima quando você só precisa ler como Object, ? extends ao ler tipos específicos e ? super ao escrever em uma coleção!
Folha de consulta
Java fornece três tipos de wildcards (curingas) para trabalhar com tipos genéricos quando o parâmetro de tipo exato é desconhecido ou irrelevante.
Wildcard Ilimitado (?)
Representa um tipo desconhecido e aceita 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>()); // FuncionaWildcard com Limite Superior (? extends Type)
Aceita o tipo especificado ou qualquer uma de suas subclasses. Use 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 DoubleWildcard com Limite Inferior (? super Type)
Aceita o tipo especificado ou qualquer uma de suas superclasses. Use 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 IntegerPrincípio PECS
Producer Extends, Consumer Super: Use extends quando você apenas lê (produtor), use super quando você apenas escreve (consumidor).
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: Imprima "Food inventory:" e depois chame printInventory
// TODO: Imprima uma linha em branco, depois "Total food items: [count]" usando calculateTotalFood
// TODO: Crie um novo ArrayList<Food> chamado meatStock
// TODO: Imprima uma linha em branco, depois "Adding to meat stock..."
// TODO: Chame addMeatToStock com meatStock e "Beef"
// TODO: Imprima "Meat stock after adding:" e depois chame printInventory em 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 Iterator