ProgrammierungBackend-Entwickler

Erzählen Sie von den wichtigsten Aspekten der Arbeit mit der Generierung und Verwendung von Generics in Java. Welche Feinheiten sind für eine sichere Anwendung wichtig?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort.

Generics ermöglichen die Erstellung von Klassen, Schnittstellen und Methoden mit Typparametern, was die Typprüfung zur Kompilierzeit sicherstellt und hilft, ClassCastException zu vermeiden.

Wichtige Merkmale und Fallstricke:

  • Type Erasure: Zum Zeitpunkt der Kompilierung wird die Information über Typparameter gelöscht, daher ist es z. B. nicht möglich, ein Array vom generischen Typ zu erstellen: new List<String>[10] — Kompilierfehler.
  • Einschränkungen bei der Verwendung:
    • Man kann keine Instanzen eines parametrierbaren Typs erstellen: T obj = new T();
    • instanceof kann nicht mit parametrierbaren Typen verwendet werden: if(obj instanceof List<String>) — Fehler.
    • Es können keine statischen Felder von parametrierbaren Typen erstellt werden.
  • Wildcards: ? extends T — Kovariante (lesen), ? super T — kontravariant (schreiben).
  • PECS-Prinzip (Producer Extends, Consumer Super): Wenn man nur lesen möchte — benutze extends, wenn man schreiben möchte — super.

Beispiel:

// Kovariante Methode zum Lesen void printNumbers(List<? extends Number> numbers) { for (Number n : numbers) { System.out.println(n); } } // Kontravariate Methode zum Schreiben void addIntegers(List<? super Integer> list) { list.add(10); } }

Trickfrage.

Frage: "Was ist der Unterschied zwischen List<Object> und List<?>? Kann man ein beliebiges Objekt in List<?> einfügen?"

Antwort: Nein, in List<?> kann man nichts hinzufügen (außer null), da der Compiler nicht weiß, welcher Typparameter dort ist. In List<Object> kann man hingegen beliebige Objekte hinzufügen.

Beispiel:

List<?> list1 = new ArrayList<String>(); // list1.add("test"); // Kompilierfehler! List<Object> list2 = new ArrayList<>(); list2.add("test"); // OK

Beispiele für reale Fehler aufgrund fehlender Kenntnisse über die Feinheiten dieses Themas.


Geschichte

Ein Entwicklerteam versuchte, einen Cache basierend auf einem Array vom parametrierten Typ T[] zu implementieren. Aufgrund der Typlöschung und der Unmöglichkeit, Arrays vom generischen Typ zu erstellen, funktionierte die Lösung nicht wie erwartet: es entstand ein Array Object[], was zu ClassCastException bei Laufzeit-Casts führte.


Geschichte

In einem der Mikrodienste versuchte ein Entwickler, einen Empfänger zu implementieren, der List<?> als Parameter verwendete, und versuchte, die Sammlung zu modifizieren. Dies führte zu einem Kompilierfehler und verzögerte den Release-Termin, da die Logik mit Berücksichtigung von PECS umgestaltet werden musste.


Geschichte

Im Projekt zur Integration mit einem externen System machte ein Entwickler einen Fehler, indem er eine Sammlung eines Typs durch eine andere über einen unbearbeiteten Raw-Type überschreibt: List list = new ArrayList<String>(), was zu ClassCastException und Abstürzen des Dienstes in der Produktion führte, als versucht wurde, die Elemente in andere Typen zu konvertieren.