Innere und anonyme Klassen
Innere Klassen
💬 eng.: inner class oder auch nested class
Als innere Klassen werden Klassen bezeichnet, die innerhalb anderer Klassen definiert sind. Dies wird u.a. dazu genutzt, den Coder besser zu strukturieren: Manchmal ist eine Klasse semantisch so sehr einer anderen Klasse untergeordnet und wäre alleine unbrauchbar, dass sie am besten nicht in einer eigenen Datei steht:
class ContactData {
private String firstName;
private String lastName;
private Address address;
//...
class Address {
private String street;
private String city;
//...
}
}
💬 In diesem Beispiel geht es nur um die Semantik der verwendeten Attribute. In der Realität bräuchte man für die Adresse natürlich nicht unbedingt eine eigene Klasse!
In diesem Beispiel wird die Klasse Address
eventuell sogar niemals außerhalb der Klasse Customer
genutzt. Das wäre dann eigentlich ein Fall für eine private
innere Klasse, denn innere Klassen können (anders als normale Klassen) private
oder auch protected
sein (siehe auch Sichtbarkeitsmodifizierer).
Innere Klassen können aber auch “von außerhalb” genutzt werden:
class Outer {
private String s = "something";
class Inner {
public void foo(){
System.out.println(s);
}
}
}
Von einer anderen Klasse aus ließe sich nun folgendes tun:
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.foo();
}
Hier sehen wir gleich mehrere interessante Dinge:
1) Wir benötigen eine Instanz von Outer
, um eine Instanz von Inner
erzeugen zu können (es sei denn Inner
ist static
- denn auch das ist möglich!).
2) Von Inner
aus kann auf Attribute und Methoden von Outer
zugegriffen werden!
Wäre Inner
eine statische innere Klasse …
class Outer {
static class Inner {
// ...
}
}
… dann wäre der Zugriff auf Inner
auch ohne eine Instanz von Outer
möglich (ähnlich wie bei statischen Methoden oder Klassenvariablen):
Outer.Inner inner = new Outer.Inner();
⚠️ Natürlich kann eine statische innere Klasse auch nur auf statische 👉 Member der äußeren Klasse zugreifen!
🔗 Eine hübsche Übersicht zu inneren Klassen (mit praktischen Beispielen und Code zum Ausführen) findet man in diesem W3Schools-Artikel.
Anonyme Klassen
Eine anonyme Klasse trägt keinen Namen und wird in ein und dem selben Statement deklariert und instanziiert. Da in Java jedes Objekt aber eine klare Typ-Zuordnung braucht, muss diese anonyme Klasse entweder eine bestehende Klasse erweitern oder ein Interface implementieren. Die Syntax ist in beiden Fällen gleich, da weder extends
noch implements
verwendet werden.
Nehmen wir folgende Klasse Foo
an:
public class Foo {
public void saySomething() {
System.out.println("foo");
}
}
Indem wir eine anonyme Klasse verwenden, können wir sehr schnell und ohne eine neue .java
Datei anzulegen eine “Wegwerf”-Erweiterung von Foo
mit überschriebener Methode foo()
deklarieren und sofort eine Instanz davon erzeugen:
// Instanz von "Foo" erzeugen
Foo foo = new Foo();
// Instanz von "Bar" (Erweiterung von "Foo") erzeugen
Foo bar = new Foo() {
@Override
public void saySomething() {
System.out.println("bar");
}
};
foo.saySomething();
bar.saySomething();
Die Ausgabe dieses Codes wäre - wenig überraschend:
foo
bar
Diese anonyme Klasse lässt sich (durch den fehlenden Namen) nur ein einziges mal instanziieren. Es sei denn, die Methode, in der das geschieht, wird mehrmals aufgerufen - aber in diesem Fall ließe sich wohl darüber diskutieren, ob es sich dann nicht eigentlich um einmalige Instanzen unterschiedlicher Klassen mit identischer Deklaration handelt.
⚠️ Wäre
Foo
in diesem Beispiel ein Interface, sähe der Code zur Deklaration der anonymen Klasse (dieFoo
implementiert) exakt genauso aus! Anonyme Klassen erweitern oder implementieren Klassen oder Interfaces also implizit - je nach dem, worum es sich im Einzelfall handelt.
Ein sehr häufiger Anwendungsfall für anonyme Klassen in Java sind 🔗 Listener für Buttons (o.ä.) in 👉 GUIs, bei denen zum Zeitpunkt der Initialisierung noch schnell die Methode überschrieben werden soll, die ausgeführt wird, wenn das jeweilige Ereignis (Klick o.ä.) eingetreten ist. Das sieht dann z.B. so aus:
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
button.setText("I was clicked!");
}
});
🔗 Eine gute weiterführende Ressource (in englischer Sprache) ist dieser Artikel.