CodeGym /Kurse /JAVA 25 SELF /Generics: warum man sie braucht, grundlegende Syntax

Generics: warum man sie braucht, grundlegende Syntax

JAVA 25 SELF
Level 26 , Lektion 4
Verfügbar

1. Das Problem „roher“ Collections (Raw Types)

Kurz ein Blick in die Geschichte. Vor Java 5 waren alle Collections „allesfressend“. Sie speicherten Objekte vom Typ Object, und der Compiler kontrollierte nicht, was genau Sie hineinlegten. Möchten Sie einen String ablegen? Bitte. Eine Zahl? Warum nicht. Eine Katze? Geht auch.

// Beispiel "roher" Collections (Raw Types), Java bis Version 5
List list = new ArrayList();
list.add("Hallo");
list.add(42);
list.add(new Object());

Das Problem zeigte sich beim „Herausnehmen“ und Verwenden des Werts:

String s = (String) list.get(0); // OK, das ist ein String
String s2 = (String) list.get(1); // BUMM! ClassCastException

Der Compiler schweigt, und zur Laufzeit erhalten Sie eine ClassCastException. Das ist wie eine Schachtel mit der Aufschrift „Äpfel“, in der eine Tasse, eine Banane und ein Igel liegen.

Warum ist das schlecht?

  • Fehler zeigen sich erst zur Laufzeit.
  • Typen werden vermischt: Objekte müssen manuell auf den benötigten Typ gecastet werden.
  • Der Code ist weniger lesbar und riskanter.

Lösung – Generics (generische Typen)

Generics (generische Typen) sind ein Mechanismus, mit dem man Klassen, Interfaces und Methoden mit Typparametern erstellen kann. Das heißt, Sie sagen der Collection: „Speichere nur Strings“, und der Compiler achtet strikt darauf.

List<String> words = new ArrayList<>();
words.add("Hallo");
words.add("Welt");
// words.add(42); // Kompilierfehler! In List<String> kann man keinen int hinzufügen

Jetzt lässt der Compiler nicht zu, dass in die Liste etwas anderes als Strings gelegt wird. Der Fehler wird vor dem Start des Programms abgefangen.

Hauptidee von Generics:
Typsicherheit für Collections (und mehr) gewährleisten, damit Fehler beim Kompilieren statt zur Laufzeit gefunden werden.

2. Syntax von Generics: wie das im Code aussieht

Typangabe in spitzen Klammern

Wenn Sie eine Collection erstellen, geben Sie den Elementtyp in <> an:

List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
// names.add(123); // Fehler: Eine Zahl kann nicht zu einer String-Liste hinzugefügt werden

String first = names.get(0); // Kein Cast nötig!

Klassiker:

  • List<String> — Liste von Strings
  • List<Integer> — Liste ganzer Zahlen
  • Set<Double> — Menge von Gleitkommazahlen
  • Map<String, Integer> — Schlüssel String, Wert Integer

Warum nicht einfach nur List schreiben?

Kann man, aber man verliert alle Vorteile von Generics, und der Compiler warnt:

List list = new ArrayList(); // raw type — nicht empfohlen!
list.add("Hello");
list.add(7.5);
String s = (String) list.get(1); // Hallo, ClassCastException!

Moderner Java-Code verwendet immer Generics.

Diamond-Operator <>

Seit Java 7 muss man den Typ rechts nicht mehr angeben, wenn er aus dem Kontext klar ist:

List<String> list = new ArrayList<>(); // Der Compiler erkennt hier selbst, dass es sich um <String> handelt

3. Generics für verschiedene Collections

Beispiele für List, Set, Map

List<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);

Set<String> uniqueNames = new HashSet<>();
uniqueNames.add("Alice");
uniqueNames.add("Bob");

Map<String, Integer> ages = new HashMap<>();
ages.put("Alice", 23);
ages.put("Bob", 31);

Beispiel mit eigener Klasse

class Student {
    String name;
    int age;
    // ...
}

List<Student> students = new ArrayList<>();
students.add(new Student());

4. Nützliche Details

Vorteile von Generics

Typsicherheit. Der Compiler stellt sicher, dass in die Collection nur Elemente des gewünschten Typs gelangen.

Kein Cast mehr nötig. Früher: String s = (String) list.get(0);. Jetzt: String s = list.get(0);.

Der Code ist lesbarer und zuverlässiger. Weniger Überraschungen zur Laufzeit.

Einschränkungen von Generics

Primitive Typen können nicht verwendet werden. Generics arbeiten nur mit Objekten, nicht mit Primitiven (int, double, boolean). Verwenden Sie Wrapper-Klassen: Integer, Double, Boolean.

List<Integer> numbers = new ArrayList<>();
numbers.add(10); // int wird automatisch in Integer umgewandelt (Autoboxing)

Kurz zur Typenlöschung (type erasure)

In Java sind Generics über den Mechanismus der Typenlöschung implementiert: Nach der Kompilierung wird die Information über Typparameter gelöscht, und zur Laufzeit weiß die JVM nicht, dass es List<String> war und nicht einfach List. Das geschieht aus Gründen der Abwärtskompatibilität.

Folge: Man kann den Typparameter nicht mit instanceof zusammen mit einem konkreten Typargument prüfen.

List<String> list = new ArrayList<>();
// if (list instanceof List<String>) { ... } // Kompilierfehler!

Der Versuch, ein Element eines anderen Typs hinzuzufügen – ein Kompilierfehler

List<String> words = new ArrayList<>();
words.add("Hello");
// words.add(123); // Kompilierfehler: incompatible types: int cannot be converted to String

Map<String, Integer> map = new HashMap<>();
map.put("Katze", 5);
// map.put(3, "Elefant"); // Fehler: Der Schlüssel muss String sein, der Wert — Integer

Und das ist großartig: Fehler werden bereits beim Kompilieren gefunden.

Nicht nur Collections

Generics kann man in eigenen Klassen und Methoden verwenden. Zum Beispiel eine universelle „Box“:

class Box<T> {
    private T value;

    public void set(T value) { this.value = value; }
    public T get() { return value; }
}

Box<String> stringBox = new Box<>();
stringBox.set("Hallo");
System.out.println(stringBox.get());

Box<Integer> intBox = new Box<>();
intBox.set(42);
System.out.println(intBox.get());

Bei Collections sind Generics Standard, aber Sie begegnen ihnen auch an anderen Stellen, z. B. in Stream API und Optional.

5. Typische Fehler im Umgang mit Generics

Fehler Nr. 1: Verwendung von „rohen“ Collections. Eine Zuweisung wie List list = new ArrayList(); nimmt Ihnen die Typsicherheit. Geben Sie immer Typparameter an, z. B. List<String>.

Fehler Nr. 2: Versuch, Primitive zu verwenden. Man kann nicht List<int> schreiben, verwenden Sie List<Integer>.

Fehler Nr. 3: Manuelles Casten beim Lesen aus der Collection. Wenn Sie Generics verwenden, ist ein Cast wie (String) list.get(i) nicht nötig. Wenn Sie ihn brauchen, haben Sie irgendwo die Typen verletzt.

Fehler Nr. 4: Erwartung, dass Typparameter zur Laufzeit verfügbar sind. Wegen der Typenlöschung kann man sie nicht mit instanceof à la List<String> prüfen.

Fehler Nr. 5: Verschiedene Typen in einer Collection mischen. Wenn List<String> deklariert ist, fügen Sie keinen Integer hinzu – der Compiler lässt das nicht zu, und das ist gut so.

1
Umfrage/Quiz
Collections und generics, Level 26, Lektion 4
Nicht verfügbar
Collections und generics
Collections und generics
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION