Einführung in Generics
Teil des Abschnitts Objektorientierte Programmierung der Java-Journey von Coddy. Lektion 53 von 87.
Stell dir vor, du musst eine Klasse erstellen, die einen Wert speichert und ihn später abruft. Ohne Generics müsstest du separate Klassen für jeden Typ schreiben: eine für Integer, eine für String, eine für Double und so weiter. Das führt zu doppeltem Code und erheblichen Wartungsproblemen.
Generics lösen dieses Problem, indem sie dir ermöglichen, Klassen und Methoden zu schreiben, die mit jedem Typ funktionieren und dabei dennoch die Typsicherheit gewährleisten. Du definierst einen Platzhalter (genannt Typparameter), der durch einen tatsächlichen Typ ersetzt wird, wenn du die Klasse verwendest:
// Ohne Generics - verwendet Object, erfordert Casting
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0); // Manuelles Casting erforderlich
// Mit Generics - typsicher, kein Casting
List<String> list = new ArrayList<String>();
list.add("Hello");
String s = list.get(0); // Kein Casting erforderlichDie spitzen Klammern <T> bezeichnen einen Typ-Parameter. Wenn du List<String> schreibst, teilst du dem Compiler mit, dass diese Liste nur Zeichenketten enthalten wird. Wenn du versuchst, eine Ganzzahl zu add, fängt der Compiler den Fehler sofort ab, anstatt erst zur Laufzeit fehlzuschlagen.
Generics bieten zwei entscheidende Vorteile: Sie machen explizite Typumwandlungen überflüssig und erkennen Typfehler zur Kompilierzeit statt zur Laufzeit. Dadurch wird dein Code sowohl sicherer als auch übersichtlicher. In den folgenden Lektionen werden wir untersuchen, wie du deine eigenen generischen Klassen und Methoden erstellst.
Aufgabe
EinfachLass uns ein einfaches Speichersystem erstellen, das die Leistungsfähigkeit von Generics demonstriert, indem es die alte Vorgehensweise (mit Object und Casting) mit der neuen Vorgehensweise (mit Generics und Typsicherheit) vergleicht.
Du wirst drei Dateien erstellen, um den Unterschied direkt zu sehen:
OldBox.java: Erstelle eine Klasse, die Werte auf die Art vor Generics speichert. Deine OldBox sollte ein privates Feldcontentvom TypObjectbesitzen. Füge einen Konstruktor hinzu, der ein Object akzeptiert und speichert, sowie einegetContent()-Methode, die das Object zurückgibt. Dieser Ansatz funktioniert, erfordert aber beim Abrufen der Werte ein Casting.GenericBox.java: Erstelle nun die moderne, typsichere Version mit Generics. Deine GenericBox sollte in ihrer Deklaration den TypparameterTverwenden. Sie benötigt ein privates Feldcontentvom TypT, einen Konstruktor, der einen Wert vom TypTakzeptiert, sowie einegetContent()-Methode, die den TypTzurückgibt. Bei der Verwendung dieser Klasse ist kein Casting erforderlich!Main.java: Zeige beide Ansätze nebeneinander. Du erhältst zwei Eingaben: ein Wort (String) und eine Zahl (Ganzzahl).Verwende zuerst den alten Ansatz: Erstelle eine OldBox, in der das Wort gespeichert wird, rufe es mit einem Cast zu String ab und gib Folgendes aus:
OldBox (with cast): [value]Verwende als Nächstes den generischen Ansatz: Erstelle eine
GenericBox<String>, in der dasselbe Wort gespeichert wird, rufe es ohne Casting ab und gib Folgendes aus:GenericBox (no cast): [value]Zeige anschließend, dass Generics mit Ganzzahlen funktionieren: Erstelle eine
GenericBox<Integer>, in der die Zahl gespeichert wird, rufe sie ab und gib Folgendes aus:GenericBox Integer: [value]Gib schließlich eine leere Zeile aus, gefolgt von:
Type safety: Generics catch errors at compile time!
Du erhältst zwei Eingaben in dieser Reihenfolge: ein Wort (String) und eine Zahl (Ganzzahl als String, die du parsen wirst).
Beachte den entscheidenden Unterschied: Bei OldBox musst du (String) box.getContent() casten und hoffen, dass der Typ stimmt. Bei GenericBox kennt der Compiler den Typ, und es ist kein Casting erforderlich. Das ist der zentrale Vorteil von Generics: Typsicherheit ohne den Aufwand des Castings!
Probier es selbst
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String word = scanner.nextLine();
int number = Integer.parseInt(scanner.nextLine());
// TODO: Erstelle eine OldBox, die das Wort speichert
// Retrieve with cast to String and print: "OldBox (with cast): [value]"
// TODO: Erstelle eine GenericBox<String>, die das Wort speichert
// Retrieve without casting and print: "GenericBox (no cast): [value]"
// TODO: Create a GenericBox<Integer> storing the number
// Retrieve and print: "GenericBox Integer: [value]"
// TODO: Gib eine leere Zeile aus, dann gib aus:
// "Type safety: Generics catch errors at compile time!"
}
}
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