Alle Tabs der Lerneinheit (Erklärung · Interaktiv verstehen · Praxis-Übung · Klausur-Quiz) als durchgehender Text. Ideal zum Wiederholen vor der Klausur, und für Suchmaschinen wie Google, Bing und KI-Suche (ChatGPT, Perplexity).
Diese Lerneinheit wurde für typische Bachelor-Klausuren konzipiert. So prüfen wir · Fehler entdeckt? Melde ihn uns oder markiere die fragliche Stelle direkt im Text oben.
Alle Tabs der Lerneinheit (Erklärung · Interaktiv verstehen · Praxis-Übung · Klausur-Quiz) als durchgehender Text. Ideal zum Wiederholen vor der Klausur, und für Suchmaschinen wie Google, Bing und KI-Suche (ChatGPT, Perplexity).
List<String>, Map<Integer, Student>, Optional<T> — diese eckigen Klammern sind Generics. Sie sagen dem Compiler: "Diese Liste enthält NUR Strings, kein Integer, kein Object." Damit fängt Java Tippfehler beim Kompilieren statt erst zur Laufzeit mit einem ClassCastException.
Klausur-Pflicht in 10/13 WInf-Prog-1-Klausuren — Type-Parameter, Wildcards, Type-Erasure sind Standard-Stoff.
Klausur-Tipp: Bei Klausurfragen "Welche Zeile wirft einen Compile-Fehler?" — schau auf den deklarierten Generic-Typ und prüfe, ob der zugewiesene Wert/Typ passt. Generics-Fehler werden vom Compiler erkannt, NICHT erst zur Laufzeit.
Anmelden, um den Fortschritt zu speichern.
Nächster Schritt
Aktives Abrufen festigt Wissen schneller als nochmal lesen.
List<String>, Map<Integer, Student>, Optional<T> — diese eckigen Klammern sind Generics. Sie sagen dem Compiler: "Diese Liste enthält NUR Strings, kein Integer, kein Object." Damit fängt Java Tippfehler beim Kompilieren statt erst zur Laufzeit mit einem ClassCastException.
Klausur-Pflicht in 10/13 WInf-Prog-1-Klausuren — Type-Parameter, Wildcards, Type-Erasure sind Standard-Stoff.
Generics: Du parametrisierst eine Klasse oder Methode mit einem Typ, statt einen festen Typ hart zu kodieren.
Box<T>ist eine Box für irgendeinen Typ T, der beim Anlegen festgelegt wird.
Vor Java 5 gab es keine Generics. Listen sahen so aus:
List liste = new ArrayList();
liste.add("Hallo");
liste.add(42); // kein Fehler! Objekt = alles geht
String s = (String) liste.get(1); // CRASH zur Laufzeit
Die Liste konnte alles speichern, weil intern Object. Beim Rausholen musstest du casten — und wenn du falsch lagst, ClassCastException zur Laufzeit. Bug findet dich, nicht du den Bug.
Mit Generics:
List<String> liste = new ArrayList<>();
liste.add("Hallo");
liste.add(42); // ← Compile-Fehler! int passt nicht in List<String>
String s = liste.get(0); // kein Cast nötig
Type-Safety zur Compile-Zeit. Der ganze Vorteil von Generics in einem Satz.
Box<T>public class Box<T> {
private T inhalt;
public void setInhalt(T inhalt) {
this.inhalt = inhalt;
}
public T getInhalt() {
return inhalt;
}
}
Nutzen:
Box<String> textBox = new Box<>();
textBox.setInhalt("Hallo");
String s = textBox.getInhalt(); // kein Cast
Box<Integer> zahlBox = new Box<>();
zahlBox.setInhalt(42);
// zahlBox.setInhalt("Text"); // Compile-Fehler!
Das T ist nur ein Platzhalter. Beim Anlegen (new Box<String>()) wird er ersetzt. Konvention: ein Buchstabe (T für Type, E für Element, K/V für Key/Value).
Auch einzelne Methoden können generisch sein, unabhängig von der Klasse:
public class Util {
public static <T> T erstes(List<T> liste) {
return liste.get(0);
}
}
String s = Util.<String>erstes(myStringList); // explizit
String s2 = Util.erstes(myStringList); // Java schließt T = String
Das <T> vor dem Rückgabetyp deklariert den Type-Parameter dieser Methode.
T extends Comparable<T>Manchmal brauchst du eine Bedingung an T — z.B. "T muss vergleichbar sein":
public static <T extends Comparable<T>> T max(List<T> liste) {
T grösstes = liste.get(0);
for (T element : liste) {
if (element.compareTo(grösstes) > 0) {
grösstes = element;
}
}
return grösstes;
}
Jetzt darf T nur etwas sein, das Comparable ist — und du darfst compareTo aufrufen.
extends heißt hier sowohl "erbt von" als auch "implementiert" (Interfaces). Java spart sich ein zweites Wort für Interfaces in Generics-Bounds.
<?>, <? extends X>, <? super X>Wildcards braucht man bei Methoden-Parametern, wenn der konkrete Typ egal sein darf:
| Wildcard | Bedeutung | Beispiel |
|---|---|---|
List<?> | Liste von irgendwas (read-only) | universelle Ausgabe-Funktion |
List<? extends Number> | Liste von Number oder Subklassen (Integer, Double) | nur lesen ("Producer") |
List<? super Integer> | Liste von Integer oder Oberklassen (Number, Object) | nur schreiben ("Consumer") |
PECS-Regel: Producer Extends, Consumer Super. Wenn die Liste dir Werte liefert, nutze extends. Wenn du Werte reinschreibst, nutze super.
Im fertigen Bytecode verschwindet das <T>! Java ersetzt T durch Object (oder bei Bounds durch die obere Schranke) und fügt Casts ein, wo nötig. List<String> und List<Integer> sind zur Laufzeit dieselbe Klasse.
Konsequenzen:
List<String> l1 = new ArrayList<>();
List<Integer> l2 = new ArrayList<>();
l1.getClass() == l2.getClass(); // true!
// und das hier ist KEIN Compile-Fehler:
if (obj instanceof List) { ... } // erlaubt
// aber das hier IST ein Compile-Fehler:
if (obj instanceof List<String>) { ... } // nicht erlaubt
Du kannst auch keine Generic-Arrays anlegen:
T[] array = new T[10]; // Compile-Fehler
List<String>[] arr = new List<String>[5]; // Compile-Fehler
1. List<String> heißt: NUR Strings. Compile-Time-Garantie, kein Cast beim Rausholen.
2. <T> ist ein Platzhalter. Konkretisiert beim Anlegen: new Box<Integer>().
3. Diamond-Operator <> (Java 7+): rechts kannst du den Typ weglassen, Compiler schließt ihn. Map<String, List<Integer>> m = new HashMap<>();
4. Type-Erasure. Zur Laufzeit existiert das <T> nicht mehr. getClass() zeigt nur ArrayList, nicht ArrayList<String>.
5. PECS-Regel. Producer extends, Consumer super. Wenn unsicher: ohne Wildcard erstmal probieren, beim Methoden-Übergreifen Wildcards einsetzen.
1. new T() versuchen. Geht nicht, weil T zur Laufzeit nicht existiert. Workaround: T als Class<T> Parameter mitgeben oder Supplier<T> verwenden.
2. List<Integer> an List<Number> Parameter übergeben. Geht nicht! Generics sind invariant: List<Integer> ist NICHT subtyp von List<Number>, obwohl Integer extends Number. Wenn du das brauchst: Wildcard List<? extends Number>.
3. Primitive Typen in Generics. List<int> ist Compile-Fehler. Generics brauchen Object-Typen. Nutze die Wrapper: List<Integer>, List<Double>. Autoboxing macht den Rest.
4. Diamond <> vor Java 7 vergessen. In modernem Code IMMER mit Diamond schreiben, sonst Raw-Type-Warnung.
5. Raw-Types verwenden. List l = new ArrayList(); (ohne <...>) ist erlaubt aber deprecated und löst Warnings aus. Vermeiden.
Probier verschiedene Typ-Parameter aus. Beobachte: identischer Code, andere Typen — Compile-Fehler erscheinen / verschwinden.
Wähle den Typ T für Box<T> aus und sieh, welche Operationen erlaubt sind und welche der Compiler ablehnt.
Interaktive Visualisierung
Interaktive Komponente: probiere sie im Topic-Player oben aus.
Klausur-Tipp: Bei Klausurfragen "Welche Zeile wirft einen Compile-Fehler?" — schau auf den deklarierten Generic-Typ und prüfe, ob der zugewiesene Wert/Typ passt. Generics-Fehler werden vom Compiler erkannt, NICHT erst zur Laufzeit.
6 Aufgaben zu Type-Parameter, Wildcards, Type-Erasure.
Klausurfragen mit Lösungen (6)
Antwort: Type-Safety zur Compile-Zeit
Erklärung: Generics geben dir Type-Safety BEIM KOMPILIEREN. Type-Mismatches werden vom Compiler erkannt, statt erst zur Laufzeit als ClassCastException zu crashen. Keine Performance-Vorteile (eher gleich), Speicher unverändert.
List<String> liste = new ArrayList<>();
liste.add("Hallo");
liste.add(42);
String s = liste.get(0);Antwort: Zeile 3: liste.add(42)
Erklärung: Zeile 3 ist ein Compile-Fehler: 42 (Integer) passt nicht in List<String>. Das ist exakt der Schutz, den Generics geben. Zeile 4 wäre ohne Generics ein Cast (String) — mit Generics nicht nötig.
Antwort: Falsch
Erklärung: FALSCH. Type-Erasure: zur Laufzeit sind beide einfach ArrayList. Die Typ-Information ist nur zur Compile-Zeit da. Deshalb: liste.getClass() == liste2.getClass() ist TRUE, auch bei unterschiedlichen Generic-Typen.
Typ: Wahr/Falsch
Antwort: Nein, Generics sind invariant
Erklärung: Generics sind INVARIANT — auch wenn Integer Subtyp von Number ist, ist List<Integer> KEIN Subtyp von List<Number>. Wäre fatal, weil du sonst einen Double in eine List<Integer> reinschmuggeln könntest. Wenn du das brauchst: Wildcard List<? extends Number>.
Richtige Antworten: List<?> erlaubt jeden Typ als Inhalt; List<? extends Number> erlaubt nur Lesen, kein Schreiben (außer null); List<? super Integer> erlaubt das Hinzufügen von Integer-Werten; PECS: Producer extends, Consumer super
Erklärung: Richtig: List<?> Beliebig, extends → nur lesen, super → schreiben ok, PECS-Regel. Falsch: Wildcards sind Compile-Time, keine Perf-Auswirkung. List<?> != List<Object>: List<String> ist Subtyp von List<?> aber NICHT von List<Object>.
Typ: Multi-Select
Zuordnungen:
Erklärung: Standard-Generic-Vokabular. Sitzen lernen, kommt in 1-2 Klausurfragen vor.
Typ: Zuordnung
Klausurfragen mit Lösungen (6)
Antwort: E
Erklärung: E = Element (List<E>, Set<E>). T = generischer Type, K = Key, V = Value (Map<K,V>). Nur Konvention, technisch wäre jeder Buchstabe erlaubt.
Antwort: Diamond: Compiler schließt den Typ aus dem linken Kontext
Erklärung: Seit Java 7 darf der Typ auf der rechten Seite weggelassen werden, wenn er aus dem linken Kontext (Variable-Deklaration) ableitbar ist. Compiler ergänzt automatisch <String>.
Antwort: Falsch
Erklärung: FALSCH. Wegen Type-Erasure ist das Compile-Fehler. Java weiß zur Laufzeit nicht, was T war, und kann das Array nicht typsicher allokieren. Workaround: List<T> verwenden oder (T[]) Array.newInstance(...).
Typ: Wahr/Falsch
Antwort: <T> void kopiere(List<? extends T> src, List<? super T> dst)
Erklärung: PECS in Aktion! src ist PRODUCER → extends (src.get(i) liefert T oder Subtyp). dst ist CONSUMER → super (dst.add() akzeptiert T oder Subtyp). Klassische Java-API-Signatur (siehe Collections.copy).
Lösungen pro Lücke:
Erklärung: Generics-Kern-Vokabular. Invariant, Type-Erasure, PECS — wer diese 3 Begriffe sicher erklären kann, hat Generics verstanden.
Typ: Lückentext
Richtige Reihenfolge:
Erklärung: Compile-Zeit: Typ-Check. Bytecode-Generierung: Erasure + Cast-Insertion. Laufzeit: nur noch Object. So liefert Java Type-Safety ohne JVM-Generics.
Typ: Reihenfolge