Generics in Java: warum sie nötig sind und wie man sie für Anfänger verwendet
Beim Erlernen von Java werden Sie früher oder später auf das Konzept der Generics stoßen. Für Anfänger mag dies kompliziert erscheinen, aber es ist tatsächlich eines der nützlichsten Werkzeuge der Sprache. In diesem Artikel werden wir erklären, was Generics sind, warum sie nötig sind und wie man sie richtig verwendet. Das Material richtet sich an Anfänger-Entwickler.
Was sind Generics und warum ist es ohne sie schlecht?
Generics (Verallgemeinerungen) sind ein Java-Mechanismus, der es ermöglicht, Typen zu parametrisieren. Einfach ausgedrückt: Sie können eine Klasse, ein Interface oder eine Methode schreiben, die mit verschiedenen Datentypen arbeitet, dabei aber eine strenge Typisierung zur Kompilierzeit beibehält.
Stellen Sie sich vor, Sie möchten eine Box erstellen, in die Sie jeden beliebigen Gegenstand legen können. Ohne Generics müssten Sie Object verwenden:
public class Box { private Object item;
public void put(Object item) { this.item = item; }
public Object get() { return item; }}Die Verwendung einer solchen Klasse ist eine wahre Qual:
Box box = new Box();box.put("Hallo");// Beim Abrufen muss der Typ explizit gecastet werdenString text = (String) box.get(); // Risiko einer ClassCastException!Wenn Sie versehentlich eine Zahl hineinlegen und dann versuchen, sie in einen String zu casten, stürzt das Programm zur Laufzeit mit einem Fehler ab. Generics lösen dieses Problem, indem sie die Typprüfung auf die Kompilierzeit verlagern.
Wie man Generics verwendet: grundlegende Syntax
Lassen Sie uns unsere Klasse Box mit Generics umschreiben:
public class Box<T> { private T item;
public void put(T item) { this.item = item; }
public T get() { return item; }}Hier ist <T> der Typparameter. T ist ein allgemein üblicher Name (vom Wort "Type"), aber Sie können jeden beliebigen Buchstaben oder jedes Wort verwenden: E (Element), K (Key), V (Value) usw.
Jetzt ist die Verwendung sicher und elegant:
Box<String> stringBox = new Box<>();stringBox.put("Hallo");String text = stringBox.get(); // Kein Casting nötig!
// Diese Zeile wird nicht kompilieren:// stringBox.put(123); // Kompilierfehler!Der Compiler weiß selbst, dass stringBox nur mit Strings arbeitet. Wenn Sie versuchen, eine Zahl hineinzulegen, wird der Code nicht einmal kompilieren – Sie erfahren sofort von dem Fehler, nicht erst zur Laufzeit.
Generics in Collections: wo sie am meisten benötigt werden
Der häufigste Einsatzort für Generics sind Java-Collections (List, Set, Map und andere). Vor dem Aufkommen von Generics (in Java 5) quälten sich Entwickler mit ArrayList, die Object speicherte:
// Alter Stil (ohne Generics)List list = new ArrayList();list.add("Hallo");list.add(123); // Das kann auch hinzugefügt werden!
String item = (String) list.get(0); // In OrdnungString item2 = (String) list.get(1); // ClassCastException!Mit Generics ist alles einfacher und sicherer:
List<String> names = new ArrayList<>();names.add("Alice");names.add("Bob");// names.add(123); // Kompilierfehler
for (String name : names) { System.out.println(name.toUpperCase()); // Kein Casting nötig!}Das Gleiche funktioniert für Map:
Map<String, Integer> ageMap = new HashMap<>();ageMap.put("Alice", 30);ageMap.put("Bob", 25);
int aliceAge = ageMap.get("Alice"); // Automatisches Unboxing zu intEinschränkungen und Wildcards: wie man Generics flexibel macht
Manchmal müssen Sie einschränken, welche Typen ein Generic akzeptieren kann. Dafür werden bounded type parameters (eingeschränkte Typparameter) verwendet.
Zum Beispiel