Wildcards (?, extends, super)
Teil des Abschnitts Objektorientierte Programmierung der Java-Journey von Coddy — Lektion 57 von 87.
Eingeschränkte Typparameter beschränken Generics auf bestimmte Typen, aber was ist, wenn Sie mit vorhandenen generischen Typen arbeiten und den genauen Typparameter nicht kennen – oder er Ihnen egal ist? Hier kommen Wildcards ins Spiel.
Die unbeschränkte Wildcard ? repräsentiert einen unbekannten Typ. Sie ist nützlich, wenn Sie jeden beliebigen parametrisierten Typ akzeptieren möchten:
public static void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}
printList(new ArrayList<String>()); // Funktioniert
printList(new ArrayList<Integer>()); // FunktioniertDie nach oben beschränkte Wildcard (upper bounded wildcard) ? extends Type akzeptiert den angegebenen Typ oder eine seiner Unterklassen. Verwenden Sie sie, wenn Sie aus einer generischen Struktur lesen müssen:
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 von Integer
sumNumbers(Arrays.asList(1.5, 2.5)); // Liste von DoubleDie nach unten beschränkte Wildcard ? super Type akzeptiert den angegebenen Typ oder eine seiner Superklassen. Verwenden Sie sie, wenn Sie in eine generische Struktur schreiben müssen:
public static void addIntegers(List<? super Integer> list) {
list.add(1);
list.add(2);
}
List<Number> numbers = new ArrayList<>();
addIntegers(numbers); // Funktioniert - Number ist ein Supertyp von IntegerEine hilfreiche Richtlinie: Verwenden Sie extends, wenn Sie nur lesen (Producer), verwenden Sie super, wenn Sie nur schreiben (Consumer). Dies ist als das PECS-Prinzip bekannt – Producer Extends, Consumer Super.
Aufgabe
EinfachLassen Sie uns ein Zoo-Fütterungssystem bauen, das alle drei Arten von Wildcards demonstriert! Sie werden eine Hierarchie von Tieren und Futter erstellen und dann Hilfsmethoden schreiben, die ungebundene (unbounded), nach oben begrenzte (upper bounded) und nach unten begrenzte (lower bounded) Wildcards verwenden, um verschiedene Fütterungsszenarien zu handhaben.
Sie werden Ihren Code über vier Dateien organisieren:
Food.java: Erstellen Sie eine einfache Klassenhierarchie für Futter. Beginnen Sie mit einer BasisklasseFood, die ein privates Feldname(String), einen Konstruktor zu dessen Initialisierung und einegetName()-Methode besitzt. Erstellen Sie dann zwei Unterklassen in derselben Datei:Meaterweitert Food (der Konstruktor ruftsuper(name)auf) undVegetableerweitert Food (gleiches Muster).Animal.java: Erstellen Sie eine BasisklasseAnimalmit einem privaten Feldspecies(String), einem Konstruktor und einergetSpecies()-Methode. Erstellen Sie zwei Unterklassen:CarnivoreundHerbivore, die jeweils Animal erweitern und den Eltern-Konstruktor aufrufen.ZooFeeder.java: Hier glänzen die Wildcards! Erstellen Sie eine Hilfsklasse mit drei statischen Methoden:printInventory(List<?> items)- Verwendet eine ungebundene Wildcard, um eine beliebige Liste auszugeben. Durchlaufen Sie die Liste in einer Schleife und geben Sie jedes Element mittoString()aus, eines pro Zeile.calculateTotalFood(List<? extends Food> foods)- Verwendet eine nach oben begrenzte Wildcard, um aus einer Liste eines beliebigen Food-Typs zu lesen. Geben Sie die Anzahl der Elemente alsintzurück. Dies demonstriert das Lesen von einem Producer.addMeatToStock(List<? super Meat> stock, String meatName)- Verwendet eine nach unten begrenzte Wildcard, um in eine Liste zu schreiben, die Meat aufnehmen kann. Erstellen Sie ein neuesMeat-Objekt mit dem angegebenen Namen und fügen Sie es dem Bestand hinzu. Dies demonstriert das Schreiben in einen Consumer.Main.java: Bringen Sie Ihr Zoo-Fütterungssystem zusammen! Sie erhalten zwei Eingaben: einen Fleisch-Namen (String) und einen Gemüse-Namen (String).Erstellen Sie zuerst eine
ArrayList<Food>und fügen Sie einMeat-Objekt mit dem Fleisch-Namen und einVegetable-Objekt mit dem Gemüse-Namen hinzu. Geben SieFood inventory:aus und rufen Sie dannprintInventorymit dieser Liste auf.Geben Sie als Nächstes eine Leerzeile aus, und drucken Sie dann
Total food items: [count]unter Verwendung voncalculateTotalFoodauf Ihrer Futterliste aus.Erstellen Sie schließlich eine neue
ArrayList<Food>namensmeatStock. Geben Sie eine Leerzeile aus, dannAdding to meat stock.... Rufen SieaddMeatToStockmit dieser Liste und dem String"Beef"auf. Geben Sie dannMeat stock after adding:aus und rufen SieprintInventoryfür den Fleischbestand auf.
Sie erhalten zwei Eingaben in der Reihenfolge: einen Fleisch-Namen und einen Gemüse-Namen.
Überschreiben Sie toString() in Ihrer Food-Klasse, um den Namen des Futters zurückzugeben, damit die Ausgabe korrekt funktioniert. Beachten Sie, wie jeder Wildcard-Typ einem anderen Zweck dient: ? für maximale Flexibilität, wenn Sie nur als Object lesen müssen, ? extends beim Lesen spezifischer Typen und ? super beim Schreiben in eine Collection!
Spickzettel
Java bietet drei Arten von Wildcards für die Arbeit mit generischen Typen an, wenn der genaue Typparameter unbekannt oder irrelevant ist.
Unbegrenzte Wildcard (?)
Repräsentiert einen unbekannten Typ und akzeptiert jeden parametrisierten Typ:
public static void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}
printList(new ArrayList<String>()); // Works
printList(new ArrayList<Integer>()); // WorksObere begrenzte Wildcard (? extends Type)
Akzeptiert den angegebenen Typ oder eine seiner Unterklassen. Verwenden Sie dies, wenn Sie aus einer generischen Struktur lesen müssen:
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)); // List of Integer
sumNumbers(Arrays.asList(1.5, 2.5)); // List of DoubleUntere begrenzte Wildcard (? super Type)
Akzeptiert den angegebenen Typ oder eine seiner Superklassen. Verwenden Sie dies, wenn Sie in eine generische Struktur schreiben müssen:
public static void addIntegers(List<? super Integer> list) {
list.add(1);
list.add(2);
}
List<Number> numbers = new ArrayList<>();
addIntegers(numbers); // Works - Number is a supertype of IntegerPECS-Prinzip
Producer Extends, Consumer Super: Verwenden Sie extends, wenn Sie nur lesen (Producer), verwenden Sie super, wenn Sie nur schreiben (Consumer).
Probier es selbst
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: Erstelle eine ArrayList<Food> und füge Meat- und Vegetable-Objekte hinzu
// TODO: Gib "Food inventory:" aus und rufe dann printInventory auf
// TODO: Gib eine Leerzeile aus, dann "Total food items: [count]" unter Verwendung von calculateTotalFood
// TODO: Erstelle eine neue ArrayList<Food> namens meatStock
// TODO: Gib eine Leerzeile aus, dann "Adding to meat stock..."
// TODO: Rufe addMeatToStock mit meatStock und "Beef" auf
// TODO: Gib "Meat stock after adding:" aus und rufe dann printInventory für meatStock auf
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienEinführung in die OOPKlassen vs. ObjekteDas this-SchlüsselwortMethodenFelder (Attribute)Konstruktor-MethodeKonstruktor-ÜberladungRückblick – Einfacher Taschenrechner4Vererbung
Grundlagen der Vererbung (extends)Das super-SchlüsselwortMethoden überschreiben (@Override)Konstruktor-VerkettungDie Object-KlasseEinfache & mehrstufige VererbungWarum keine Mehrfachvererbung von KlassenZusammenfassung - Mitarbeiter-Hierarchie7Spezielle Methoden & Object-Klasse
toString()-Methodeequals() und hashCode()clone()-MethodecompareTo() und ComparableComparator-InterfaceZusammenfassung – Benutzerdefiniertes Sortieren2Zugriffsmodifikatoren & Kapselung
Überblick über ZugriffsebenenGetter- und Setter-MethodenInformation HidingDas final-SchlüsselwortRückblick – Bankkonto-Manager5Polymorphismus
Grundlagen der MethodenüberladungMethodenüberschreibung (Laufzeit)Upcasting und DowncastingDer instanceof-OperatorAbstrakte Klassen und MethodenZusammenfassung – Formenrechner8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungAggregation vs. KompositionInnere, geschachtelte & anonyme KlassenEnums und Enum-MethodenRecords (Java 16+)Sealed Classes (Java 17+)11Design Patterns Teil 1
Einführung in Design PatternsSingleton-PatternFactory-PatternBuilder-PatternObserver-PatternStrategy-Pattern14Abschlussherausforderungen
E-Learning-PlattformBanksystemSpielcharakter-EntwicklungFahrzeugvermietung3Klasseneigenschaften & Statische Member
Instanz- vs. statische VariablenStatische MethodenStatische BlöckeKonstanten (static final)Zusammenfassung – Zähler & Utility6Interfaces & Abstrakte Klassen
Einführung in InterfacesInterfaces implementierenImplementierung mehrerer InterfacesDefault- & statische Methoden in InterfacesAbstrakte Klassen vs. InterfacesFunktionale InterfacesZusammenfassung - Zahlungssystem9Generics
Einführung in GenericsGenerische KlassenGenerische MethodenEingeschränkte TypparameterWildcards (?, extends, super)Zusammenfassung – Generische Container