Wildcards (?, extends, super)
Teil des Abschnitts Objektorientierte Programmierung der Java-Journey von Coddy. Lektion 57 von 87.
Gebundene Typparameter beschränken Generics auf bestimmte Typen, aber was ist, wenn du mit vorhandenen generischen Typen arbeitest und den genauen Typparameter nicht kennst oder er dir egal ist? Hier kommen Wildcards ins Spiel.
Der unbegrenzte Wildcard-Typ ? stellt einen unbekannten Typ dar. Er ist nützlich, wenn du jeden parametrisierten Typ akzeptieren möchtest:
public static void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}
printList(new ArrayList<String>()); // Funktioniert
printList(new ArrayList<Integer>()); // FunktioniertDer Wildcard mit oberer Schranke ? extends Type akzeptiert den angegebenen Typ oder eine seiner Unterklassen. Verwende ihn, wenn du aus einer generischen Struktur lesen musst:
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 DoubleDer untere begrenzte Wildcard ? super Type akzeptiert den angegebenen Typ oder eine seiner Oberklassen. Verwende ihn, wenn du in eine generische Struktur schreiben musst:
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: Verwende extends, wenn du nur liest (Produzent), und verwende super, wenn du nur schreibst (Konsument). Dies ist als PECS-Prinzip bekannt: Producer Extends, Consumer Super.
Aufgabe
EinfachLass uns ein Zoo-Fütterungssystem erstellen, das alle drei Arten von Wildcards demonstriert! Du wirst eine Hierarchie von Tieren und Futter erstellen und anschließend Hilfsmethoden schreiben, die unbeschränkte, nach oben beschränkte und nach unten beschränkte Wildcards verwenden, um verschiedene Fütterungsszenarien zu verarbeiten.
Du wirst deinen Code auf vier Dateien verteilen:
Food.java: Erstelle eine einfache Klassenhierarchie für Futter. Beginne mit einer BasisklasseFood, die ein privates Feldname(String), einen Konstruktor zu dessen Initialisierung und einegetName()-Methode besitzt. Erstelle anschließend zwei Unterklassen in derselben Datei:Meaterweitert Food (der Konstruktor ruftsuper(name)auf) undVegetableerweitert Food (nach demselben Muster).Animal.java: Erstelle eine BasisklasseAnimalmit einem privaten Feldspecies(String), einem Konstruktor und einergetSpecies()-Methode. Erstelle zwei Unterklassen:CarnivoreundHerbivore, die jeweils Animal erweitern und den Konstruktor der übergeordneten Klasse aufrufen.ZooFeeder.java: Hier kommen Wildcards zur Geltung! Erstelle eine Hilfsklasse mit drei statischen Methoden:printInventory(List<?> items)– Verwendet eine unbeschränkte Wildcard, um eine beliebige Liste auszugeben. Durchlaufe die Liste und gib jedes Element mithilfe vontoString()jeweils in einer eigenen Zeile aus.calculateTotalFood(List<? extends Food> foods)– Verwendet eine nach oben beschränkte Wildcard, um aus einer Liste eines beliebigen Food-Typs zu lesen. Gib die Anzahl der Elemente alsintzurück. Dies demonstriert das Lesen aus einem Produzenten.addMeatToStock(List<? super Meat> stock, String meatName)– Verwendet eine nach unten beschränkte Wildcard, um in eine Liste zu schreiben, die Meat aufnehmen kann. Erstelle ein neuesMeat-Objekt mit dem angegebenen Namen und füge es dem Bestand hinzu. Dies demonstriert das Schreiben in einen Konsumenten.Main.java: Führe dein Zoo-Fütterungssystem zusammen! Du erhältst zwei Eingaben: einen Fleischnamen (String) und einen Gemüsenamen (String).Erstelle zunächst eine
ArrayList<Food>und füge einMeat-Objekt mit dem Fleischnamen sowie einVegetable-Objekt mit dem Gemüsenamen hinzu. GibFood inventory:aus und rufe anschließendprintInventorymit dieser Liste auf.Gib danach eine leere Zeile aus und anschließend
Total food items: [count], wobei ducalculateTotalFoodauf deine Futterliste anwendest.Erstelle schließlich eine neue
ArrayList<Food>mit dem NamenmeatStock. Gib eine leere Zeile und anschließendAdding to meat stock...aus. RufeaddMeatToStockmit dieser Liste und der Zeichenkette"Beef"auf. Gib danachMeat stock after adding:aus und rufeprintInventoryfür den Fleischbestand auf.
Du erhältst zwei Eingaben in dieser Reihenfolge: einen Fleischnamen und einen Gemüsenamen.
Überschreibe toString() in deiner Food-Klasse so, dass der Name des Futters zurückgegeben wird und die Ausgabe korrekt funktioniert. Beachte, wie jeder Wildcard-Typ einem anderen Zweck dient: ? für maximale Flexibilität, wenn du lediglich als Object lesen musst, ? extends beim Lesen spezifischer Typen und ? super beim Schreiben in eine Collection!
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: Print "Food inventory:" then call printInventory
// TODO: Print blank line, then "Total food items: [count]" using calculateTotalFood
// TODO: Erstelle neue ArrayList<Food> namens meatStock
// TODO: Print blank line, then "Adding to meat stock..."
// TODO: Rufe addMeatToStock mit meatStock und "Beef" auf
// TODO: Print "Meat stock after adding:" then call printInventory on meatStock
}
}
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 Schlüsselwort thisMethodenFelder (Attribute)KonstruktormethodeÜberladen von KonstruktorenRückblick – Einfacher Taschenrechner4Vererbung
Grundlegende Vererbung (extends)Das Schlüsselwort superÜberschreiben von Methoden (@Override)KonstruktorverkettungDie Object-KlasseEinfach- und mehrstufige VererbungWarum keine Mehrfachvererbung von KlassenRückblick – Mitarbeiterhierarchie7Spezielle Methoden & Object-Klasse
toString()-Methodeequals() und hashCode()clone()-MethodecompareTo() und ComparableComparator-SchnittstelleRückblick – Benutzerdefinierte Sortierung2Zugriffsmodifikatoren & Kapselung
Übersicht der ZugriffsebenenGetter- und Setter-MethodenInformationsverbergungDas Schlüsselwort finalRückblick – Bankkontoverwaltung5Polymorphismus
Grundlagen der MethodenüberladungMethodenüberschreibung (Laufzeit)Upcasting und DowncastingDer instanceof-OperatorAbstrakte Klassen und MethodenRückblick – Formrechner8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungAggregation vs. KompositionInnere, verschachtelte & anonyme KlassenEnums und Enum-MethodenRecords (Java 16+)Versiegelte Klassen (Java 17+)11Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterBuilder-MusterObserver-MusterStrategie-Muster14Abschließende Herausforderungen
E-Learning-PlattformBankingsystemEntwicklung einer SpielfigurFahrzeugvermietungsdienst3Klassen-Props & statische Member
Instanz- vs. statische VariablenStatische MethodenStatische BlöckeKonstanten (static final)Wiederholung – Counter & Utility6Schnittstellen & abstrakte Klassen
Einführung in InterfacesInterfaces implementierenMehrere Interfaces implementierenDefault & Static in InterfacesAbstrakte Klassen vs. InterfacesFunktionale InterfacesRückblick – Zahlungssystem9Generics
Einführung in GenericsGenerische KlassenGenerische MethodenGebundene TypparameterWildcards (?, extends, super)Rückblick – Generischer ContainerÜbe selbstständig: Online-Java-Compiler