Eine Klasse innerhalb einer anderen zu deklarieren ist Alltag. Ob davor static
steht, halten viele für Geschmackssache. Es ist der Unterschied zwischen zwei
grundverschiedenen Dingen.
Zwei Dinge, die gleich aussehen
Eine statisch verschachtelte Klasse ist eine gewöhnliche Klasse, die
zufällig innen steht. Ohne static wird daraus eine innere Klasse — und
deren Instanzen existieren immer im Zusammenhang mit einer bestimmten Instanz
der umgebenden Klasse. Sie tragen einen verborgenen Verweis darauf, den niemand
geschrieben hat und den man im Quelltext nicht sieht.
Das erklärt den Komfort: Eine innere Klasse kommt ohne Parameter an die Felder
ihres Erzeugers. Ausschreiben lässt sich der Verweis trotzdem — als
Aussen.this, und erzeugen mit aussen.new Innen(). Ohne äußere Instanz gibt
es keine innere.
Der Preis steht nicht im Code
Solange die innere Instanz lebt, kann die äußere nicht freigegeben werden. Und
beim Serialisieren wandert sie mit in den Datenstrom — samt allem, woran sie
hängt. In einem Dienst mit einem Dutzend Abhängigkeiten zieht ein kleines Objekt
so den halben Anwendungskontext nach. Genau diesen Effekt erzeugt ein vergessenes
static: Der Code übersetzt anstandslos und funktioniert — bis er es nicht mehr
tut.
Die Frage, die genügt
Bei Schnittstellen, Enums und Records stellt sich die Frage gar nicht: Sie sind
verschachtelt immer statisch. Bleibt die Klasse. Und dafür reicht eine Frage:
Braucht dieser Typ den Zustand der äußeren Instanz? Wenn nein, gehört static
davor. Nichtstatisch ist die begründungspflichtige Variante, nicht umgekehrt.
Mehr dazu im Seminar
Wie sich das an einem durchgehenden Beispiel auflöst — vom Builder als statisch verschachtelter Klasse bis zu anonymen Klassen und der Regel „effektiv final” — zeigt das Modul Innere Typen.