Inner Classes
Inner Classes
Innere Klasse (inner class) ist Überbegriff für
- Nichtstatische Elementklassen (nonstatic member classes)
- Lokale Klassen (local classes)
- Anonyme Klassen (anonymous classes)
Gemeinsamkeiten
- Durften bis Java 15 keine statischen Variablen oder Methoden enthalten
- Können
static final
Felder (Konstanten) enthalten - Haben eine implizite Referenz auf ein Objekt der umgebenden Klasse (Details folgen)
Der Begriff innere Klasse bezeichnet eine Gruppe von geschachtelten Klassen, die sich dadurch auszeichnen, dass sie eine Beziehung zu einem Objekt der umgebenden Klasse haben. Diese Besonderheit wird noch detailliert erläutert.
Bis Java 16 durften die Klassen dieser Kategorie keine statischen Methoden oder Attribute enthalten. Ausgenommen hiervon waren nur Konstanten, also static final
Attribute. Seit Java 16 ist diese Einschränkung gefallen.
title: “Nonstatic Member Classes” nav_order: 4 —
Nonstatic Member Classes
Nicht-statische Elementklassen
Nichtstatische Elementklassen (nonstatic member classes)
- Deklaration einer (nicht-statischen) Klasse innerhalb einer Klasse aber außerhalb einer Methode
- Hat Zugriff auf alle Attribute (statisch oder nicht-statisch) und Methoden (statisch oder nicht-statisch) der äußeren Klasse
- Name ist in der ganzen Klasse und abhängig von der Sichtbarkeit auch außerhalb bekannt
class A {
class B { }
}
Der Zugriff auf die Methoden und Felder ist unabhängig von der Sichtbarkeit dieser. D. h. eine non-static member class kann auch auf private Methoden und Felder der äußeren Klasse zugreifen.
- Kann nur im Kontext eines Objekts der äußeren Klasse existieren
Outer outerObject = new Outer();
Outer.Inner ref = outerObject.new Inner();
- Hat eine implizite Referenz auf das Objekt der äußeren Klasse, das bei der Erzeugung benutzt wurde
Outer.this
Auf den ersten Blick sieht die Forderung „die innere Klasse hat Zugriff auf die Attribute der umgebenden Klasse“ einfach aus. Wenn man sich aber vor Augen führt, dass die Attribute in einem Objekt der umgebenden Klasse gespeichert sind, kann der Zugriff nur dann möglich sein, wenn die innere Klasse das Objekt auch kennt. Sie benötigt also eine Referenz auf das Objekt der umgebenden Klasse, genauso wie Methoden eine this
-Referenz benötigen, um auf die Daten des Objektes zuzugreifen. Über diese Referenz können dann die Methoden der inneren Klasse auf die Attribute zugreifen.
Würde man jetzt statische Methoden in der inneren Klasse erlauben, müsste sogar die innere Klasse selbst ein Objekt der äußeren Klasse kennen. Dies wäre aber eine paradoxe Konstruktion, denn welches der vielen Objekte, die von der äußeren Klasse im Laufe der Zeit angelegt werden, soll denn die innere Klasse erhalten? Da man diesen Widerspruch nicht auflösen kann, sind statische Methoden in inneren Klassen grundsätzlich verboten. Da statische Methoden aber verboten sind, wird die Konstruktion etwas einfacher.
Jedes Objekt der inneren Klasse besitzt eine implizite Referenz auf ein Objekt der äußeren Klasse. Über diese kann es dann in seinen nicht-statischen Methoden auf die Attribute des Objektes zugreifen. Der Zugriff auf die this-Referenz der äußeren Klasse erfolgt über die besondere Form Outer.this
statt nur einfach this
.
Als nächstes stellt sich die Frage, wie diese implizite Referenz entsteht. Die Lösung ist, dem Objekt der inneren Klasse bei seiner Erzeugung diese Referenz mitzugeben. Entweder indem man sie explizit dem new
-Operator mitgibt (siehe nächstes Beispiel) oder weil die Erzeugung in einer Methode der äußeren Klasse erfolgt, wodurch automatisch klar ist, welches Objekt verwendet werden soll (die this
-Referenz der Methode).
Beispiel: Implizite Referenz
public class Computer {
String hersteller = "HP";
class Hauptspeicher {
int groesse = 1024;
}
}
Computer computer = new Computer();
Computer.Hauptspeicher h = computer.new Hauptspeicher();
Um der inneren Klasse Zugriff auf die Daten eines Objektes der äußeren Klasse zu erlauben, werden die inneren Objekte immer im Kontext eines äußeren Objektes erzeugt. Man erkennt dies daran, dass das new
auf einem Objekt der äußeren Klasse aufgerufen wird. Diese Referenz heißt in der inneren Klasse dann <Name der auesseren Klasse>.this
, hier im Beispiel also Computer.this
.
Im Bild erkennt man deutlich die Verknüpfung zwischen dem Objekt der inneren Klasse und dem Objekt der äußeren Klasse über die Referenz Compuer.this
. Diese wurde nirgends deklariert, entsteht aber implizit durch die Beziehung zwischen innerer und äußerer Klasse bei der Erzeugung des inneren Objektes.
public class Computer {
String hersteller = "HP";
class Hauptspeicher {
int groesse = 1024;
}
}
Computer computer = new Computer();
Computer.Hauptspeicher h1 = computer.new Hauptspeicher();
Computer.Hauptspeicher h2 = computer.new Hauptspeicher();
Wenn mehrere Objekte der inneren Klasse im Kontext eines Objektes der äußeren Klasse erzeugt werden, dann teilen sie sich dieses Objekt, d. h. ihre <outer>.this
-Referenzen zeigen auf dasselbe Objekt.
public class Computer {
String hersteller = "HP";
Hauptspeicher speicher = new Hauptspeicher();
class Hauptspeicher {
int groesse = 1024;
void printHersteller() {
System.out.println(
Computer.this.hersteller);
}
}
}
Computer computer = new Computer();
Wenn das Objekt der inneren Klasse innerhalb einer Methode oder eines Konstruktors der äußeren Klasse erzeugt wird, kann man sich die besondere Form des new-Operators sparen. Hier reicht ein einfaches new
, da klar ist, in Kontext welchen Objektes die Instanz der inneren Klasse erzeugt wird. In diesem Beispiel ist das new Hauptspeicher()
gleichbedeutend mit this.new Hauptspeicher()
.
public class Computer {
Hauptspeicher hauptspeicher = new Hauptspeicher();
class Hauptspeicher {
Speicherzelle zelle = new Speicherzelle();
class Speicherzelle {
}
}
}
Computer computer = new Computer();
Wenn Klassen über mehrere Stufen ineinander geschachtelt werden, dann hat die innerste Klasse Zugriff auf die Instanzvariablen (und damit Objekte) aller sie umgebenden Klassen. Um dies zu gewährleisten, besitzt sie mehrere spezielle this-Referenzen und zwar eine pro Ebene. Diese Effekt kann man im Beispiel gut an der Klasse Speicherzelle sehen, die sowohl eine Hauptspeicher.this
- als auch eine Computer.this
-Referenz besitzt.
Es ist wichtig zu beachten, dass die Syntax für die this-Referenzen nicht hierarchisch ist, sondern flach: es heißt nicht Computer.Hauptspeicher.this
, sondern nur Hauptspeicher.this
. Hier wird jetzt auch klar, warum die inneren Klassen unterschiedliche Namen haben müssen. Andernfalls wäre unklar, welches Objekt gemeint ist.
Beispiel: Zugriff auf implizites this
public class Computer {
String hersteller = "HP";
class Hauptspeicher {
int groesse = 1024;
class Speicherzelle {
int kapazitaet = 10;
void doIt() {
System.out.println(Computer.this.hersteller);
System.out.println(Hauptspeicher.this.groesse);
System.out.println(kapazitaet);
}
}
}
}
Hier ist noch einmal der Zugriff auf die speziellen this-Referenzen zu sehen und zwar in der qualifizierten Form.
public class Computer {
String hersteller = "HP";
class Hauptspeicher {
int groesse = 1024;
class Speicherzelle {
int kapazitaet = 10;
void doIt() {
System.out.println(hersteller);
System.out.println(groesse);
System.out.println(kapazitaet);
}
}
}
}
Wenn die Namen von Membern eindeutig sind und nicht überdeckt werden, kann man die spezielle this-Referenz weglassen und nur den Namen verwenden, da der Compiler problemlos feststellen kann woher die Variable stammt. Hier ist es also egal, ob man hersteller
oder Computer.this.hersteller
schreibt. Ersteres wird vom Compiler ohnehin in die zweit Form umgewandelt.
Beispiel: Erzeugen der Objekte
Computer computer = new Computer();
Computer.Hauptspeicher speicher = computer.new Hauptspeicher();
Computer.Hauptspeicher.Speicherzelle zelle = speicher.new Speicherzelle();
Computer.Hauptspeicher.Speicherzelle zelle =
new Computer().new Hauptspeicher().new Speicherzelle();
Will man die Objekte der inneren Klassen von außen erzeugen, muss man das Objekt der jeweils umgebenden Klasse beim new
mit angeben.
Man sieht hier, dass zwar die Referenzen des inneren auf das äußere Objekt nicht hierarchisch sind, wohl aber die Namen der Klassen.