Innere Klassen können ab Java JDK 1.1 definiert werden. Java-Entwickler sind vor allem aufgrund des neuen Ereignismodells in JDK 1.1 auf das Verwenden von inneren Klassen angewiesen.
Im Englischen wird eine innere Klasse meist als inner class bezeichnet, im Deutschen wird sie oft auch lokale Klasse oder anonyme Klasse genannt. In diesem Beitrag werden wir die innere Klasse in Form einer nichtstatischen lokalen Klasse behandeln. Der nachfolgende Beitrag wird sich dann der anonymen Klasse widmen.
Innere Klassen werden in Java wie normale Klassen behandelt. Vom Java-Compiler wird zu jeder inneren Klasse eine .class-Datei erzeugt. Der Dateiname setzt sich aus dem Namen der äußeren Klasse gefolgt von einem Dollarzeichen und dem Namen der inneren Klasse zusammen.
Bei anonymen inneren Klassen wird statt des Namens der inneren Klasse eine fortlaufende Nummer vom Java-Compiler vergeben.
Für die Beispielanwendung dieses Beitrags werden die folgenden Klassendateien vom Compiler erstellt:
Mit inneren Klassen können Funktionszeiger nachgebildet und dadurch Codeelemente zwischen verschiedenen Programmteilen ausgetauscht werden. Das Nachbilden von Funktionszeigern ist auch mit einem Interface möglich, wie wir in dem Beitrag: Mit Hilfe von Interfaces Funktionszeiger in Java nachbilden gezeigt haben.
Das Nutzen von Interfaces als Funktionszeiger ist sehr hilfreich, wenn die Konfigurationsanforderungen einer Methode nicht durch die Übergabe von Variablen erfüllt werden können. Dies ist bspw. bei der Programmierung von grafischen Benutzeroberflächen häufig der Fall. Dann werden solche Callback-Funktionen sehr gerne eingesetzt.
Die Vorgehensweise ist dabei wie folgt:
- Zuerst wird ein Interface definiert, in dem die erforderliche Methode abstrakt deklariert wird.
- Dieses Interface wird danach von unterschiedlichen Klassen implementiert und dabei wird die Interface-Methode jeweils so umgesetzt, dass in ihr die erforderlichen Berechnungen durchgeführt werden.
- Dadurch besitzt jede Klasse eine eigene, an die Konfigurationsanforderungen angepasste, Implementierung der Interface-Methode.
- Werden nun Instanzen dieser Klassen erzeugt und an die zu konfigurierende Methode übergeben, so wird in der zu konfigurierenden Methode jeweils genau die Methodenversion der jeweils instanziierten Klasse ausgeführt. Die jeweilige Methodenversion kann dadurch den Konfigurationsanforderungen entsprechend implementiert werden.
Was ist eine innere Klasse in Java?
Innere Klassen können in Java innerhalb von jeder beliebigen Klasse definiert werden, die damit zur äußeren Klasse wird. Die Definition einer inneren Klasse kann innerhalb der äußeren Klasse oder in einer ihrer Methoden erfolgen.
Eine innere Klasse kann nur innerhalb der äußeren Klasse instanziert werden. Dies kann während der Initialisierung der äußeren Klasse oder in einer ihrer Methoden erfolgen.
Die innere Klasse ist nur lokal sichtbar, daher werden innere Klassen in Java auch als lokale Klassen bezeichnet. Die innere Klasse kann auf alle Membervariablen und Methoden der äußeren Klasse zugreifen. Die äußere Klasse kann auf alle Membervariablen und Methoden aller inneren Klassen zugreifen.
In dem folgenden Quellcodebeispiel wird eine innere Klasse in einer äußeren Klasse definiert. Die innere Klasse ist eine nichtstatische lokale Klasse.
Beispielanwendung: Eine innere Klasse in einer äußeren Klasse
/* * Beispielanwendung: eine innere Klasse in einer äußeren Klasse */ public class OuterClass { byte idOuterClass; String ort; // Membervariable ort der OuterClass public void erzeugeInnerClassObject(String ortInnerClass) { InnerClass ic = new InnerClass(); ic.ort = ortInnerClass; gebeDetailsAus(ic); } public void gebeDetailsAus(OuterClass.InnerClass innerClassObject) { innerClassObject.gebeAus("In inner class method"); } public void gebeObjektAus(OuterClass.InnerClass innerClassObject) { System.out.println("\nObjekt: " + innerClassObject); } public void gebeIdUndOrtAus(String ort) { System.out.println(ort); } class InnerClass { private String ort; // Membervariable ort der InnerClass private void gebeAus(String ort) { String idUndOrt = "\nID: " + idOuterClass; // Membervariable der OuterClass idUndOrt += "\nOrt: " + OuterClass.this.ort; // Qualifizierung über OuterClass // OuterClass.this == oc-Objekt idUndOrt += " -> " + this.ort; // this == ic-Objekt idUndOrt += " -> " + ort; // ort == ort-Variable aus Parameterliste OuterClass.this.gebeObjektAus(this); // Aufrufen der Methode der OuterClass gebeIdUndOrtAus(idUndOrt); // Aufrufen der Methode der OuterClass } } }
Beispielanwendung: Die Testklasse für die innere Klasse
/* * Beispielanwendung: die Testklasse für die innere Klasse */ public class InnerOuterTest { public static void main(String[] args) { OuterClass oc = new OuterClass(); // das oc-Objekt oc.ort = "In outer class"; oc.idOuterClass = 5; oc.erzeugeInnerClassObject("In inner class"); } }
Unsere äußere Klasse OuterClass besitzt zwei Membervariablen idOuterClass und ort. Auf die Membervariable ort werden wir später von der inneren Klasse InnerClass zugreifen, die innerhalb der OuterClass-Klasse definiert ist.
Die äußere Klasse OuterClass besitzt vier Methoden: erzeugeInnerClassObject, gebeDetailsAus, gebeObjektAus und gebeIdUndOrtAus. Auf alle Methoden der äußeren Klasse kann auch von der inneren Klasse aus zugegriffen werden. Unsere innere Klasse InnerClass besitzt die Membervariable ort und die Methode gebeAus.
Unsere Testklasse InnerOuterTest erzeugt in der main-Methode die Instanz oc der OuterClass und initialisiert ihre beiden Membervariablen idOuterClass und ort. Danach wird über das OuterClass-Objekt oc die Methode erzeugeInnerClassObject aufgerufen.
In der Methode erzeugeInnerClassObject wird die Instanz ic der InnerClass-Klasse erzeugt und deren Membervariable ort mit dem als Argument übergebenen String ortInnerClass initialisiert. Anschließend wird die Methode gebeDetailsAus der äußeren Klasse aufgerufen und ihr das InnerClass-Objekt ic als Argument übergeben.
Die Methode gebeDetailsAus ruft über das als Argument übergebene InnerClass-Objekt ic die Methode gebeAus der inneren Klasse auf. In dieser Methode wird der String idUndOrt erzeugt. Dazu wird zuerst auf die Membervariable idOuterClass der äußeren Klasse OuterClass zugegriffen. Da keine andere Variable den selben Namen trägt, kann der Zugriff unqualifiziert erfolgen.
Dies ist für die Variable ort nicht möglich, da sie mehrfach an verschiedenen Stellen verwendet wird. Um festzustellen welche Variable verwendet werden soll, prüft er Java-Compiler zuerst, ob es eine lokale Version der Variable ort gibt (also innerhalb der Methode gebeAus). Falls diese nicht existiert, sucht er nach einer gleichnamigen Membervariable (also innerhalb der inneren Klasse InnerClass). Falls auch hier keine exisitert, sucht er nach einer gleichnamigen Membervariable eine Stufe weiter außen (also innerhalb der äußeren Klasse OuterClass).
Wenn sich gleichnamige Variablen verdecken, muss qualifiziert auf die jeweilige Variable zugegriffen werden. In unserem Fall wird wie folgt auf die Variable ort zugegriffen:
- mit
OuterClass.this.ort
greifen wir auf die Membervariable ort der äußeren Klasse zu - mit
this.ort
greifen wir auf die Membervariable ort der inneren Klasse zu - mit
ort
greifen wir auf die Methodenvariable ort aus der Parameterliste zu
Hinweis: Der Ausdruck OuterClass.this
bezeichnet das Objekt der äußeren Klasse, in der die aktuelle Instanz der inneren Klasse erzeugt wurde (hier ist es das oc-Objekt). Der Ausdruck this
bezeichnet die aktuelle Instanz der inneren Klasse (hier ist es das ic-Objekt).
Nachdem der String idUndOrt erzeugt wurde, rufen wir von der inneren Klasse die Methode gebeObjektAus der äußeren Klasse auf. Wir übergeben ihr mit this die aktuelle Instanz der inneren Klasse ic. Der Aufruf erfolgt zu Beispielzwecken qualifiziert mit OuterClass.this.gebeObjektAus(this);
. Wie im nächsten Aufruf zu sehen ist, hätte der Aufruf auch unqualifiziert erfolgen können.
Danach wird die Methode gebeIdUndOrtAus der äußeren Klasse aufgerufen. Dieser Aufruf erfolgt unqualifiziert, da die Methode nicht von einer anderen gleichnamigen Methode an anderer Stelle verdeckt wird.
Die Konsolenausgabe der Beispielanwendung ist in der unteren Abbildung dargestellt:
Innere Klassen in Methoden – Lokale nichtstatische Klassen in Methoden verwenden
Lokale nichtstatische Klassen können auch in Methoden definiert werden, also nicht nur auf der äußersten Ebene einer anderen Klasse, sondern auch innerhalb ihrer Methoden.
Dabei können die inneren Klassen in einer Methode liegen, aber auch innerhalb eines beliebigen Blocks.
Die innere Klasse kann auf die Membervariablen und Methoden der äußeren Klasse zugreifen und auf die lokalen Variablen der umschließenden Methode.
Die lokalen Variablen müssen aber Konstanten sein und daher mit dem Schlüsselwort final als konstant deklariert worden sein. Innere Klassen in Methoden werden aber nur selten von Java-Entwicklern eingesetzt.
In dem folgenden Java-Codebeispiel wird die innere Klasse InnerClassInMethodeA in einer Methode der äußeren Klasse Outer definiert. Die Testklasse InnerClassInMethodTest wird verwendet, um die Beispielanwendung zu testen.
Beispielanwendung: Eine innere Klasse in einer Methode
/* * Beispielanwendung: eine innere Klasse in einer Methode */ class Outer { int zahlOuterClass = 5; public void methodeA(int a) { int b = a; // nicht final -> daher Zugriff aus innerer Klasse nicht möglich final String textA = "\nIn MethodeA von Outer-Klasse."; // Zugriff möglich class InnerClassInMethodeA { public void gebeTextAus() { // innere Klasse kann nur auf lokale Konstanten in der MethodeA zugreifen // und auf Membervariablen und Methoden der äußeren Klasse System.out.println(textA); // textA ist als final deklariert und lokal gebeZahlAus(zahlOuterClass); // Membervariable und Methode der Outer-Klasse } } InnerClassInMethodeA icimA = new InnerClassInMethodeA(); icimA.gebeTextAus(); } public void gebeZahlAus(int a) { System.out.println(a); } } public class InnerClassInMethodTest { public static void main(String[] args) { Outer oc = new Outer(); // das oc-Objekt oc.methodeA(2014); } }
In Zeile 20 wird auf die lokale Konstante textA aus der inneren Klasse InnerClassInMethodeA heraus zugegriffen. Dies ist möglich, da die Konstante als final deklariert ist. Ein Zugriff auf die Methoden-Variable b ist nicht möglich, da sie nicht als final deklariert ist.
In Zeile 22 wird aus der inneren Klasse InnerClassInMethodeA heraus die Methode gebeZahlAus der äußeren Klasse Outer aufgerufen und ihr dabei die Membervariable zahlOuterClass als Argument übergeben. Dies ist erlaubt, da innere Klassen auf Membervariablen und Methoden der äußeren Klasse zugreifen können.
Die Konsolenausgabe der Beispielanwendung ist in der unteren Abbildung dargestellt:
Zurück zum Java-Kurs.
Comments 3
Pingback: Enumerations in Java- Aufzählungstypen definieren und erweitern
Pingback: Klassen in Java: Die Grundlagen der objektorientierten Programmierung
Pingback: Innere Klassen in Java - Statische lokale Klassen