EMZETT.
Login

Generics

Kurz: Eine Möglichkeit, Klassen und Methoden so zu schreiben, dass sie mit unterschiedlichen Datentypen arbeiten können, ohne für jeden Typ eine eigene, fast identische Version zu schreiben.

Genauer: Eine generische Liste kann z. B. mit Liste<Zahl> oder Liste<Text> genutzt werden, wobei derselbe Klassencode dahintersteckt — der konkrete Typ wird beim Verwenden festgelegt. Der große Vorteil gegenüber einer Liste, die einfach alles akzeptiert: Fehler durch falsche Typen werden bereits beim Kompilieren erkannt, statt erst zur Laufzeit einen Absturz zu verursachen.

Im Detail

Ohne Generics stünde man vor zwei schlechten Alternativen: entweder für jeden Datentyp eine fast identische, eigene Klasse schreiben (ZahlenListe, TextListe, PersonListe, …) — massive Code-Duplizierung — oder eine einzige Liste bauen, die einfach “alles” (jeden beliebigen Typ) akzeptiert, was aber Typfehler erst zur Laufzeit sichtbar macht, wenn plötzlich ein Text auftaucht, wo eine Zahl erwartet wurde.

List<String> namen = new ArrayList<>();
namen.add("Anna");
namen.add(42);        // Compilerfehler! 42 ist keine String - Fehler wird SOFORT erkannt
 
List ungeprueft = new ArrayList();   // ohne Generics: akzeptiert alles
ungeprueft.add("Anna");
ungeprueft.add(42);                    // kein Fehler beim Kompilieren...
// ... Absturz erst später, wenn der Code fälschlich eine String-Methode auf 42 aufruft

Der Zusatz in spitzen Klammern (<String>, <Zahl>) heißt Typparameter — er funktioniert konzeptionell ähnlich wie ein normaler Parameter einer Funktion, nur dass hier nicht ein Wert, sondern ein ganzer Datentyp “übergeben” wird. Der Compiler nutzt diese Information, um bei jeder Verwendung der Liste zu prüfen, ob die eingefügten bzw. gelesenen Werte tatsächlich zum angegebenen Typ passen.

Generics werden nicht nur für Collections verwendet, sondern für jede Klasse oder Methode, deren Logik unabhängig vom konkreten Datentyp funktioniert:

class Box<T> {
    private T inhalt;
    void setzeInhalt(T wert) { inhalt = wert; }
    T gibInhalt() { return inhalt; }
}
 
Box<Integer> zahlBox = new Box<>();
Box<String> textBox = new Box<>();

Hier steht T als Platzhalter für “irgendein konkreter Typ, der beim Verwenden festgelegt wird” — derselbe Box-Klassencode funktioniert für beliebige Inhalte, ohne dass für jeden möglichen Inhaltstyp eine eigene Box-Klasse geschrieben werden müsste. Dieses Prinzip — Code einmal schreiben, für viele Typen wiederverwendbar — ist eng verwandt mit Abstraktion und wird oft mit dem klassischen Interface-Konzept kombiniert, um zusätzlich einzuschränken, WELCHE Typen als Typparameter überhaupt erlaubt sind (z. B. nur Typen, die eine bestimmte Methode besitzen).

Siehe auch: Classes, List, Type Casting