Wildcards (?, extends, super)
Fait partie de la section Programmation Orientée Objet du Journey Java de Coddy — leçon 57 sur 87.
Les paramètres de type bornés restreignent les génériques à des types spécifiques, mais qu'en est-il lorsque vous travaillez avec des types génériques existants et que vous ne connaissez pas — ou ne vous souciez pas de — l'exact paramètre de type ? C'est ici que les wildcards entrent en jeu.
Le wildcard non borné ? représente un type inconnu. Il est utile lorsque vous souhaitez accepter n'importe quel type paramétré :
public static void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}
printList(new ArrayList<String>()); // Works
printList(new ArrayList<Integer>()); // WorksLe wildcard avec borne supérieure ? extends Type accepte le type spécifié ou n'importe laquelle de ses sous-classes. Utilisez-le lorsque vous avez besoin de lire à partir d'une structure générique :
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)); // Liste d'Integer
sumNumbers(Arrays.asList(1.5, 2.5)); // Liste de DoubleLe wildcard avec borne inférieure (lower bounded wildcard) ? super Type accepte le type spécifié ou n'importe laquelle de ses super-classes. Utilisez-le lorsque vous devez écrire dans une structure générique :
public static void addIntegers(List<? super Integer> list) {
list.add(1);
list.add(2);
}
List<Number> numbers = new ArrayList<>();
addIntegers(numbers); // Fonctionne - Number est un super-type d'IntegerUne règle utile : utilisez extends lorsque vous ne faites que lire (producteur), utilisez super lorsque vous ne faites qu'écrire (consommateur). C'est ce qu'on appelle le principe PECS — Producer Extends, Consumer Super.
Défi
FacileConstruisons un système de nourrissage de zoo qui illustre les trois types de jokers (wildcards) ! Vous allez créer une hiérarchie d'animaux et de nourriture, puis écrire des méthodes utilitaires qui utilisent des jokers non bornés, bornés supérieurement et bornés inférieurement pour gérer différents scénarios de nourrissage.
Vous organiserez votre code sur quatre fichiers :
Food.java: Créez une hiérarchie de classes simple pour la nourriture. Commencez par une classe de baseFoodqui possède un champ privéname(String), un constructeur pour l'initialiser et une méthodegetName(). Ensuite, créez deux sous-classes dans le même fichier :Meatétend Food (le constructeur appellesuper(name)) etVegetableétend Food (même modèle).Animal.java: Créez une classe de baseAnimalavec un champ privéspecies(String), un constructeur et une méthodegetSpecies(). Créez deux sous-classes :CarnivoreetHerbivore, chacune étendant Animal et appelant le constructeur parent.ZooFeeder.java: C'est ici que les jokers brillent ! Créez une classe utilitaire avec trois méthodes statiques :printInventory(List<?> items)- Utilise un joker non borné pour afficher n'importe quelle liste. Parcourez la liste et affichez chaque élément en utilisanttoString(), un par ligne.calculateTotalFood(List<? extends Food> foods)- Utilise un joker borné supérieurement pour lire à partir d'une liste de n'importe quel type de Food. Retourne le nombre d'éléments sous forme d'unint. Cela illustre la lecture à partir d'un producteur.addMeatToStock(List<? super Meat> stock, String meatName)- Utilise un joker borné inférieurement pour écrire dans une liste pouvant contenir de la viande (Meat). Créez un nouvel objetMeatavec le nom donné et ajoutez-le au stock. Cela illustre l'écriture dans un consommateur.Main.java: Rassemblez votre système de nourrissage de zoo ! Vous recevrez deux entrées : un nom de viande (String) et un nom de légume (String).Tout d'abord, créez une
ArrayList<Food>et ajoutez un objetMeatavec le nom de la viande et un objetVegetableavec le nom du légume. AffichezFood inventory:puis appelezprintInventoryavec cette liste.Ensuite, affichez une ligne vide, puis affichez
Total food items: [count]en utilisantcalculateTotalFoodsur votre liste de nourriture.Enfin, créez une nouvelle
ArrayList<Food>appeléemeatStock. Affichez une ligne vide, puisAdding to meat stock.... AppelezaddMeatToStockavec cette liste et la chaîne"Beef". Ensuite, affichezMeat stock after adding:et appelezprintInventorysur le stock de viande.
Vous recevrez deux entrées dans l'ordre : un nom de viande et un nom de légume.
Redéfinissez toString() dans votre classe Food pour qu'elle retourne le nom de la nourriture, afin que l'affichage fonctionne correctement. Remarquez comment chaque type de joker remplit un rôle différent : ? pour une flexibilité maximale lorsque vous avez seulement besoin de lire en tant qu'Object, ? extends lors de la lecture de types spécifiques, et ? super lors de l'écriture dans une collection !
Essayez vous-même
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: Créer une ArrayList<Food> et ajouter des objets Meat et Vegetable
// TODO: Afficher "Food inventory:" puis appeler printInventory
// TODO: Afficher une ligne vide, puis "Total food items: [count]" en utilisant calculateTotalFood
// TODO: Créer une nouvelle ArrayList<Food> nommée meatStock
// TODO: Afficher une ligne vide, puis "Adding to meat stock..."
// TODO: Appeler addMeatToStock avec meatStock et "Beef"
// TODO: Afficher "Meat stock after adding:" puis appeler printInventory sur meatStock
}
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO
Fichiers externesIntroduction à la POOClasses vs ObjetsLe mot-clé thisMéthodesChamps (Attributs)Méthode constructeurSurcharge de constructeurRécapitulatif - Calculatrice simple4Héritage
Héritage de base (extends)Le mot-clé superRedéfinition de méthode (@Override)Chaînage de constructeursLa classe ObjectHéritage simple et multiniveauPourquoi pas d'héritage multiple de classesRécapitulatif - Hiérarchie des employés7Méthodes spéciales et classe Object
Méthode toString()equals() et hashCode()Méthode clone()compareTo() et ComparableInterface ComparatorRécapitulatif - Tri personnalisé2Modificateurs d'accès et Encapsulation
Aperçu des niveaux d'accèsMéthodes Getter et SetterMasquage d'informationsLe mot-clé finalRécapitulatif - Gestionnaire de compte bancaire5Polymorphisme
Bases de la surcharge de méthodesRedéfinition de méthodes (Run-Time)Upcasting et DowncastingL'opérateur instanceofClasses et méthodes abstraitesRécapitulatif - Calculateur de formes8Concepts avancés de la POO
Composition vs HéritageAgrégation vs CompositionClasses internes, imbriquées et anonymesEnums et méthodes d'EnumRecords (Java 16+)Classes scellées (Java 17+)11Patrons de conception, partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FabriquePatron MonteurPatron ObservateurPatron Stratégie3Propriétés de classe et membres statiques
Variables d'instance vs variables statiquesMéthodes statiquesBlocs statiquesConstantes (static final)Récapitulatif - Compteur et utilitaire6Interfaces et Classes Abstraites
Introduction aux InterfacesImplémentation d'InterfacesImplémentation d'Interfaces MultiplesDefault et Static dans les InterfacesClasses Abstraites vs InterfacesInterfaces FonctionnellesRécapitulatif - Système de Paiement9La généricité
Introduction à la généricitéClasses génériquesMéthodes génériquesParamètres de type bornésWildcards (?, extends, super)Récapitulatif - Conteneur générique12Patrons de conception, partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositePatron Itérateur