Der Spickzettel zum Seminar Java SE Fortgeschrittene Sprachfeatures — für
den Moment, in dem der Compiler eine Wildcard ablehnt, capture#1 of ? in der
Meldung steht oder die Frage aufkommt, welche Retention es noch mal brauchte.
Er sammelt die Regeln, nicht die Herleitung: Warum sie so sind und wie man
damit modelliert, ist die Leistung des Seminars. Maßgeblich bleibt die
Java-Sprachspezifikation. Jeder Abschnitt verweist auf sein Modul.
Records: was der Compiler erzeugt
| Erzeugt wird | Eigenschaft |
|---|---|
| Ein Feld je Komponente | private und final |
| Der kanonische Konstruktor | Parameter = Komponenten in gleicher Reihenfolge |
| Je eine Zugriffsmethode | heißt wie die Komponente, ohne get-Präfix |
| equals und hashCode | vergleichen bzw. verrechnen alle Komponenten |
| toString | listet alle Komponenten mit Werten auf |
| Regel für Konstruktoren | Folge im Alltag |
|---|---|
| Nur der kanonische Konstruktor beschreibt Felder | Jede Prüfung gehört genau dorthin |
Zusatzkonstruktoren beginnen mit this(...) | Vorbelegungen laufen immer durch die Prüfung |
| Sichtbarkeit nicht enger als der Record selbst | Public Record heißt public Konstruktor |
| Kompaktform ohne Parameterliste | Zuweisungen ergänzt der Compiler |
public record Artikel(String nummer, List<String> etiketten) {
public Artikel { // kompakte Form
if (nummer == null || nummer.isBlank())
throw new IllegalArgumentException("nummer");
etiketten = List.copyOf(etiketten); // Parameter, nicht Feld
}
}
In der Kompaktform wird dem Parameter zugewiesen, nicht dem Feld — die
Zuweisung ans final-Feld ergänzt der Compiler am Ende. Records sind nur flach
unveränderlich: Eine Komponente vom Typ ArrayList bleibt von außen änderbar,
solange keine defensive Kopie erfolgt. Erlaubt sind eigene Methoden und
statische Felder, weitere Instanzfelder nicht.
Modul: Records
Sealed: Hierarchien abschließen
| Mittel | Erlaubt | Aussage |
|---|---|---|
| final | keine Ableitung | Hier ist Schluss |
| offen | jede Ableitung | Erweiterung erwünscht |
| Konstruktor paketprivat | Ableitung im Paket | Nebenwirkung, kein Entwurf |
sealed mit permits | genannte Typen | Die Menge ist vollständig |
| Regel, die der Compiler erzwingt | Grund |
|---|---|
Jeder Untertyp ist final, sealed oder non-sealed | Sonst ließe sich die Sperre umgehen |
| Untertypen im selben Modul bzw. Paket | Die Menge muss übersetzbar bekannt sein |
permits entfällt bei einer Quelldatei | Der Compiler sieht die Untertypen ohnehin |
| Records sind implizit final | Sie können nicht sealed sein, aber implementieren |
public sealed interface Position
permits Artikelposition, Frachtzuschlag { }
non-sealed öffnet einen Zweig ausdrücklich wieder — und gibt für ihn die
Vollständigkeitsprüfung auf.
Modul: Sealed Classes
Datenmodell: UND und ODER
| Baustein | Beziehung | Ausdrucksmittel |
|---|---|---|
| Produkttyp | UND | Record mit Komponenten |
| Summentyp | ODER | Versiegelte Klasse oder Schnittstelle |
| Summentyp | ODER | Enum bei festen Einzelwerten |
return switch (position) {
case Artikelposition(_, _, int menge, BigDecimal preis)
-> preis.multiply(BigDecimal.valueOf(menge));
case Frachtzuschlag(_, BigDecimal betrag) -> betrag;
};
Kein default-Zweig: Bei einem versiegelten Typ prüft der Compiler die
Vollständigkeit, und ein Sammelzweig schaltet genau diese Prüfung ab. Record-Muster
zerlegen die Variante beim Treffen; _ steht für Nicht-Gebrauchtes.
| Frage | Daten und Verhalten getrennt | Verhalten in der Variante |
|---|---|---|
| Neue Auswertung | ein neuer switch | Methode in jeder Variante |
| Neue Variante | jeder switch meldet sich | eine neue Klasse genügt |
| Wo steht die Logik | an einer Stelle gebündelt | über die Varianten verteilt |
Modul: Datenmodellierung
Enums: Bordmittel und Muster
| Mitglied | Art | Liefert |
|---|---|---|
values() | statisch | Feld aller Konstanten |
valueOf(String) | statisch | Konstante zum exakten Namen |
name() | Instanz | Namen der Konstante als String |
ordinal() | Instanz | Position in der Deklaration |
compareTo | Instanz | Reihenfolge der Deklaration |
valueOf findet nur über den exakten Namen — für gebundene Kürzel oder
Zahlencodes braucht es eine eigene statische Suchmethode.
| Rückgabe bei unbekanntem Wert | Passt, wenn | Kostet |
|---|---|---|
| Ausnahme werfen | Daten müssen gültig sein | Aufrufer braucht Fehlerbehandlung |
| Optional liefern | Fehlen ist normal | Aufrufer muss auspacken |
| Vorgabewert liefern | Es gibt einen sinnvollen | Fehler bleiben unsichtbar |
| null liefern | selten sinnvoll | Null-Prüfung überall |
| Verhalten je Konstante | Stärke | Schwäche |
|---|---|---|
| switch in einer Methode | alles an einer Stelle | keine Prüfung auf Vollständigkeit |
| Abstrakte Methode je Konstante | Compiler erzwingt jede | viel Rumpfcode |
| Schnittstelle implementieren | Vorgabe im Enum möglich | zusätzlicher Typ |
| Funktionales Feld | sehr kompakt | Logik nur für Einzeiler geeignet |
EnumSet speichert als Bitmuster, EnumMap nutzt die Position als Feldindex —
beide sind ihren allgemeinen Gegenstücken überlegen. Für Enums als Werte statt
als Schlüssel gibt es kein optimiertes Gegenstück.
Modul: Enum-Entwurfsmuster
Verschachtelte und innere Typen
| Verschachtelter Typ | Statisch? | Bemerkung |
|---|---|---|
Klasse mit static | ja | eigenständig, kein Verweis nach außen |
Klasse ohne static | nein | innere Klasse, an eine Instanz gebunden |
| Schnittstelle | immer | nichtstatisch gibt es nicht |
| Enum | immer | nichtstatisch gibt es nicht |
| Record | immer | nichtstatisch gibt es nicht |
| Merkmal | Statisch verschachtelt | Innere Klasse |
|---|---|---|
| Verweis nach außen | keiner | verborgen vorhanden |
| Erzeugen | new Aussen.Innen() | aussen.new Innen() |
| Zugriff auf Instanzfelder | nur über Parameter | unmittelbar |
| Serialisierung | betrifft nur den Typ | zieht die äußere Instanz mit |
Anonyme Klassen sehen zusätzlich die lokalen Variablen und Parameter ihrer Methode — sofern diese effektiv final sind. Einen Konstruktor können sie nicht haben, weil sie keinen Namen tragen.
Modul: Innere Typen
Generics: Begriffe und Schranken
| Begriff | Wertebene | Typebene |
|---|---|---|
| Deklaration | Parameter in der Signatur | Typparameter in spitzen Klammern |
| Aufruf | Argument beim Aufruf | Typargument bei der Verwendung |
| Ergebnis | initialisierter Parameter | parametrisierter Typ |
| Typ | Typparameter erlaubt? |
|---|---|
| Klasse, Schnittstelle, Record | ja |
| Enum | nein |
| Ausnahmeklassen unterhalb von Throwable | nein |
| Anonyme innere Klasse | nein, kann aber generische Typen umsetzen |
public static <T extends Comparable<T>> T groesstes(Collection<T> menge)
public static <T extends Benannt & Comparable<T>> String nameDesGroessten(…)
Schranken stehen immer mit extends, auch bei Schnittstellen — ein implements
gibt es an dieser Stelle nicht. Mehrere Schranken werden mit & verbunden;
eine Klasse darf dabei nur an erster Stelle stehen. Statische Methoden sehen
die Typparameter ihres Typs nicht — die gehören zur Instanz.
Modul: Generics
Wildcards und PECS
| Schreibweise | Bedeutung | Beispiel aus List |
|---|---|---|
? | ein beliebiger Typ | containsAll(Collection<?>) |
? extends T | T oder ein Untertyp | addAll(Collection<? extends E>) |
? super T | T oder ein Obertyp | sort(Comparator<? super E>) |
| Rolle des Parameters | Wildcard | Erlaubt im Rumpf |
|---|---|---|
| Produzent, Methode liest | ? extends T | Lesen als T |
| Konsument, Methode schreibt | ? super T | Einfügen von T |
| Beides zugleich | keine Wildcard | Typparameter verwenden |
| Frage | List<Object> | List<?> |
|---|---|---|
| Inhalt | beliebige Objekte, gemischt | Objekte eines unbekannten Typs |
| Element hinzufügen | erlaubt | nur null |
Argument List<String> | wird abgelehnt | wird angenommen |
| Einschränkung | Ausweg |
|---|---|
| Eine Wildcard hat höchstens eine Schranke | Typparameter mit mehreren Schranken |
| Obere und untere Schranke zugleich geht nicht | Signatur neu schneiden |
| Wildcards im Rückgabetyp verwerfen Information | Typparameter verwenden |
| PECS gilt nur für Parameter | Rückgabetyp getrennt entscheiden |
Eine Meldung mit capture#1 of ? heißt fast immer: Aus demselben Argument wird
gelesen und geschrieben. Dann ist ein Typparameter fällig, keine Wildcard.
Zwei Fragezeichen in einer Signatur meinen nie denselben Typ.
Modul: Wildcards
Type Erasure und ihre Folgen
| Nicht möglich | Weil zur Laufzeit |
|---|---|
new T() | der Typ T nicht bekannt ist |
x instanceof List<Zugang> | die Typargumente entfernt sind |
List<String>.class | es kein solches Klassenobjekt gibt |
| Überladen mit gleicher Löschung | beide Signaturen identisch wären |
Geprüfte Umwandlung auf List<Zugang> | die Prüfung nicht durchführbar ist |
| Form | Bedeutung | In Java |
|---|---|---|
| Invarianz | keine Beziehung zwischen den Listen | Regelfall bei Generics |
| Kovarianz | Untertyp bleibt Untertyp | über ? extends T |
| Kontravarianz | Obertyp darf einspringen | über ? super T |
| Merkmal | Array | Parametrisierter Typ |
|---|---|---|
| Elementtyp zur Laufzeit | bekannt | entfernt |
| Varianz | kovariant | invariant |
| Prüfung | zur Laufzeit | beim Übersetzen |
| Fehlerbild | ArrayStoreException | Übersetzungsfehler |
Weil Arrays reifizierbar und kovariant sind, Generics aber nicht, lässt sich
kein Array eines parametrisierten Typs erzeugen — die Meldung lautet knapp
„generic array creation”. Varargs sind Arrays und damit die Hintertür, durch
die Heap Pollution ohne Fehler und ohne Warnung entsteht. @SafeVarargs ist
eine Zusicherung des Autors: Der Compiler prüft sie nicht nach, und sie greift
nur bei finalen, statischen oder Konstruktor-Methoden.
Modul: Generics im Detail
Annotationen
| Standard-Annotation | Wirkung |
|---|---|
@Override | Fehler, wenn nichts überschrieben wird |
@Deprecated | Warnung bei jeder Verwendung |
@SuppressWarnings | Unterdrückt benannte Warnungen |
@SafeVarargs | Zusicherung bei generischen Varargs |
@FunctionalInterface | Fehler, wenn mehr als eine abstrakte Methode |
| Meta-Annotation | Legt fest |
|---|---|
@Target | An welchen Stellen die Annotation stehen darf |
@Retention | Bis wann sie aufbewahrt wird |
@Documented | Ob sie im Javadoc erscheint |
@Inherited | Ob Untertypen sie erben (nur Klassen, nicht Schnittstellen) |
@Repeatable | Ob sie mehrfach gesetzt werden darf |
| Retention | Vorhanden bis | Passend für |
|---|---|---|
| SOURCE | Ende der Übersetzung | Annotationsprozessoren |
| CLASS | in der Klassendatei | Werkzeuge, die Klassendateien lesen |
| RUNTIME | zur Laufzeit lesbar | Auswertung über Reflection |
CLASS ist die Vorgabe. Wer @Retention weglässt und zur Laufzeit sucht,
findet nichts — ohne Fehler, ohne Warnung.
| Attributtyp erlaubt | Nicht erlaubt |
|---|---|
| Primitive Typen und String | Hüllklassen wie Integer |
| Class oder Class mit Wildcard | beliebige Objekttypen |
| Enum-Typen | generische Typen wie List<String> |
| Andere Annotationstypen | null als Vorgabewert |
| Arrays der genannten Typen | Werte aus Konfigurationsdateien |
Heißt ein Attribut value und wird nur dieses gesetzt, entfällt bei der
Anwendung das value =. Alle Vorgabewerte müssen beim Übersetzen feststehen.
Modul: Annotationen
Typische Fallen
- Zugriffsmethoden eines Records mit
getbenannt — sie heißen wie die Komponente. - Im kompakten Konstruktor
this.feldzugewiesen. Das Feld ist final und wird vom Compiler beschrieben; zuweisen lässt sich nur der Parameter. - Nur beim Hineingeben kopiert und die Zugriffsmethode die Originalliste
zurückgeben lassen.
Collections.unmodifiableListist eine Sicht, keine Kopie. ordinal()in der Datenbank gespeichert und die Konstanten später umsortiert — die Zahlen ändern sich stillschweigend.- Ein veränderliches Feld im Enum. Alle Aufrufer teilen sich dieselbe Instanz; das ist globaler Zustand.
staticbei einer verschachtelten Klasse vergessen. Sie hält dann einen verborgenen Verweis auf die äußere Instanz — mit Folgen für Speicher und Serialisierung.defaultim switch über einen versiegelten Typ ergänzt, weil die IDE es vorschlägt. Damit ist die Vollständigkeitsprüfung weg.implementsstattextendsbei einer Typparameter-Schranke geschrieben.- Wildcards im Rückgabetyp verwendet und beim Aufrufer eine Umwandlung nachgezogen, statt die Signatur zu reparieren.
- Rohtypen benutzt, um einem Wildcard-Problem auszuweichen. Damit ist die Typprüfung abgeschaltet, und der Fehler erscheint erst beim Lesen.
@SuppressWarnings("unchecked")gesetzt, ohne begründen zu können, warum die Umwandlung sicher ist. Die Annotation beseitigt die Meldung, nicht das Problem.@Retentionweggelassen und sich über leere Reflection-Ergebnisse gewundert.
Dieses Thema als Schulung für Ihr Team
Dieser Beitrag erklärt das Thema. Damit Ihr Team es danach auch anwendet, gibt es Java SE Fortgeschrittene Sprachfeatures als Schulung — an Ihrem eigenen Code, mit den Fragen, die ein Text nicht beantwortet. Sie wählen die Module, wir bauen daraus ein Programm.
3 Tage·ab 900 EUR netto pro Tag (bis 3 Teilnehmende) ·Termin nach Vereinbarung
Als Team-Schulung anfragenZum Seminar Java SE Fortgeschrittene Sprachfeatures →