Menu
CoddyTech

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 für jeden Typ eine eigene Klasse schreiben: eine für Integer, eine für String, eine für Double und so weiter. Das führt zu doppeltem Code und macht die Wartung mühsam.

Generics lösen dieses Problem, indem sie dir ermöglichen, Klassen und Methoden zu schreiben, die mit jedem Typ funktionieren und dabei trotzdem die Typsicherheit gewährleisten. Du definierst einen Platzhalter (einen sogenannten Typparameter), der bei der Verwendung der Klasse durch einen tatsächlichen Typ ersetzt wird:

// Ohne Generics – verwendet Object, erfordert eine Typumwandlung
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0);  // Manuelle Typumwandlung erforderlich

// Mit Generics – typsicher, keine Typumwandlung erforderlich
List<String> list = new ArrayList<String>();
list.add("Hello");
String s = list.get(0);  // Keine Typumwandlung erforderlich

Die spitzen Klammern <T> kennzeichnen einen Typparameter. Wenn du List<String> schreibst, teilst du dem Compiler mit, dass diese Liste nur Zeichenfolgen enthält. Wenn du versuchst, eine Ganzzahl hinzuzufügen, erkennt der Compiler den Fehler sofort, anstatt dass er erst zur Laufzeit auftritt.

Generics bieten zwei entscheidende Vorteile: Sie machen explizite Typumwandlungen überflüssig und erkennen Typfehler zur Compile-Zeit statt zur Laufzeit. Dadurch wird dein Code sowohl sicherer als auch übersichtlicher. In den folgenden Lektionen sehen wir uns an, wie du eigene generische Klassen und Methoden erstellst.

challenge icon

Aufgabe

Einfach

Erstellen wir ein einfaches Speichersystem, das die Leistungsfähigkeit von Generics veranschaulicht, indem es die alte Vorgehensweise (mit Object und Typumwandlung) mit der neuen Vorgehensweise (mit Generics und Typsicherheit) vergleicht.

Du erstellst drei Dateien, um den Unterschied direkt zu sehen:

  • OldBox.java: Erstelle eine Klasse, die Werte auf die Vorgehensweise vor Generics speichert. Deine OldBox sollte ein privates Feld content vom Typ Object haben. Füge einen Konstruktor hinzu, der ein Object entgegennimmt und speichert, sowie eine Methode getContent(), die das Object zurückgibt. Diese Vorgehensweise funktioniert, erfordert aber beim Abrufen von Werten eine Typumwandlung.
  • GenericBox.java: Erstelle nun die moderne, typsichere Version mit Generics. Deine GenericBox sollte in ihrer Deklaration den Typparameter T verwenden. Sie benötigt ein privates Feld content vom Typ T, einen Konstruktor, der einen Wert vom Typ T entgegennimmt, und eine Methode getContent(), die den Typ T zurückgibt. Bei der Verwendung dieser Klasse ist keine Typumwandlung erforderlich!
  • Main.java: Veranschauliche beide Vorgehensweisen nebeneinander. Du erhältst zwei Eingaben: ein Wort (String) und eine Zahl (integer).

    Verwende zuerst die alte Vorgehensweise: Erstelle eine OldBox, in der das Wort gespeichert wird, rufe es dann mit einer Typumwandlung zu String ab und gib Folgendes aus: OldBox (with cast): [value]

    Verwende als Nächstes die generische Vorgehensweise: Erstelle eine GenericBox<String>, in der dasselbe Wort gespeichert wird, rufe es ohne Typumwandlung ab und gib Folgendes aus: GenericBox (no cast): [value]

    Zeige dann, dass Generics auch mit Ganzzahlen funktionieren: Erstelle eine GenericBox<Integer>, in der die Zahl gespeichert wird, rufe sie ab und gib Folgendes aus: GenericBox Integer: [value]

    Gib zum Schluss eine Leerzeile und danach Folgendes aus: Type safety: Generics catch errors at compile time!

Du erhältst der Reihe nach zwei Eingaben: ein Wort (String) und eine Zahl (integer als String, die du parsen wirst).

Beachte den entscheidenden Unterschied: Bei OldBox musst du (String) box.getContent() verwenden und hoffen, dass der Typ stimmt. Bei GenericBox kennt der Compiler den Typ, sodass keine Typumwandlung erforderlich ist. Das ist der entscheidende Vorteil von Generics: Typsicherheit ohne den Aufwand für Typumwandlungen!

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
        // Rufe den Wert mit einem Cast zu String ab und gib Folgendes aus: "OldBox (with cast): [value]"
        
        // TODO: Erstelle eine GenericBox<String>, die das Wort speichert
        // Rufe den Wert ohne Cast ab und gib Folgendes aus: "GenericBox (no cast): [value]"
        
        // TODO: Erstelle eine GenericBox<Integer>, die die Zahl speichert
        // Rufe den Wert ab und gib Folgendes aus: "GenericBox Integer: [value]"
        
        // TODO: Gib eine Leerzeile aus, dann gib Folgendes aus:
        // "Type safety: Generics catch errors at compile time!"
    }
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-Java-Compiler