1. Einführung in anonyme Klassen
Stell dir vor, du hast eine Klasse Animal, die beschreibt, wie sich ein Tier verhält. Diese Klasse hat eine Methode say(), die "Das Tier gibt einen Laut von sich" ausgibt. Du musst ein Objekt erstellen, das sich wie ein Hund verhält und "Wau!" ausgibt, möchtest dafür aber keine separate Datei Dog.java anlegen.
Hier kommen anonyme Klassen ins Spiel. Das sind Klassen ohne Namen, die direkt an der Verwendungsstelle deklariert und erzeugt werden. 🚀 Sie ermöglichen es, ad hoc von einer Klasse zu erben, ohne eine separate Datei zu erstellen. Anstatt viele kleine Dateien mit Klassen zu erzeugen, die nur an einer Stelle verwendet werden, kannst du die gesamte Logik genau dort schreiben, wo sie gebraucht wird.
Wie sieht die Syntax aus?
Die Syntax anonymer Klassen mag ungewohnt wirken, ist aber eigentlich recht einfach:
VariableType name = new BaseType() {
// Hier schreiben wir den Rumpf der anonymen Klasse
// Wir überschreiben die Methoden der Elternklasse
};
Schauen wir uns die Syntax genauer an:
- VariableType name: Das ist eine normale Variablendeklaration, in der wir unser neues Objekt speichern.
- new BaseType(): Hier erstellen wir scheinbar ein neues Objekt. Statt eines neuen Klassennamens verwenden wir jedoch den Namen der Klasse, von der wir erben möchten. Beachte: Nach den Klammern () steht kein Semikolon !
- { ... }: Hier öffnen wir geschweifte Klammern und schreiben die gesamte Logik unserer anonymen Klasse. Wir können Methoden der Elternklasse überschreiben oder eigene Logik hinzufügen.
Beispiel: von einer normalen Klasse erben:
Kehren wir zu unserem Beispiel mit dem Tier zurück.
class Animal {
void say() {
System.out.println("Das Tier gibt einen Laut von sich");
}
}
// Wir erstellen eine anonyme Klasse, die von Animal erbt
Animal dog = new Animal() {
// Wir überschreiben die Methode say()
@Override
void say() {
System.out.println("Wau-wau! 🐶");
}
};
Animal cat = new Animal() {
@Override
void say() {
System.out.println("Miau-miau! 🐱");
}
};
dog.say(); // Ausgabe: Wau-wau! 🐶
cat.say(); // Ausgabe: Miau-miau! 🐱
In diesem Beispiel haben wir zwei Objekte erstellt, dog und cat, die im Grunde anonyme Klassen sind, die von Animal erben. Dabei haben wir keine einzige separate Datei angelegt.
Vergleich der Ansätze
// Der übliche Weg (eigene Klasse anlegen)
class Dog extends Animal {
@Override
void say() { System.out.println("Wau!"); }
}
Animal dog = new Dog();
// VS
// Anonyme Klasse (alles an einer Stelle)
Animal dog = new Animal() {
@Override
void say() { System.out.println("Wau!"); }
};
Das Ergebnis ist dasselbe, aber die anonyme Klasse ist kürzer und „vermüllt“ das Projekt nicht.
2. Name der anonymen Klasse nach der Kompilierung
Anonyme Klassen haben im Quellcode keinen Namen, doch der Java-Compiler muss sie natürlich irgendwie benennen, um eine .class-Datei zu erzeugen. Er folgt dabei einer festen Regel:
- Der Dateiname der anonymen Klasse besteht aus dem Namen der äußeren Klasse, in der sie deklariert wurde.
- Nach dem Namen der äußeren Klasse folgt das Dollarzeichen $.
- Dann kommt die laufende Nummer der anonymen Klasse in dieser Datei, beginnend mit 1.
Wenn sich unser Beispiel mit den Tieren also in der Datei Main.java befindet, werden nach der Kompilierung drei Dateien erzeugt:
- Main.class
- Main$1.class (unsere anonyme Klasse für den Hund)
- Main$2.class (unsere anonyme Klasse für die Katze)
Wenn eine anonyme Klasse in einer Methode deklariert ist, die sich wiederum in einer inneren Klasse befindet, sieht der Name komplexer aus, z. B. OuterClass$InnerClass$1.class.
Das ist eine interne Konvention des Compilers, die nützlich zu wissen ist, im Alltag aber selten eine große Rolle spielt. Das Wichtigste ist: Eine anonyme Klasse ist trotzdem eine vollwertige Klasse, auch wenn sie im Quelltext keinen Namen hat.
3. Wichtige Besonderheiten und Einschränkungen
Anonyme Klassen sind ein mächtiges Werkzeug, aber sie haben ihre eigenen Regeln.
Zugriff auf Variablen. Eine anonyme Klasse kann Variablen aus der sie umgebenden Methode verwenden. Diese Variablen müssen jedoch final oder effectively final sein (d. h. ihr Wert wird nach der Initialisierung nicht mehr verändert).
public void doSomething() {
String greeting = "Hallo!"; // Diese Variable ist effectively final
class OuterClass {
void greet() {
// Wir erzeugen eine anonyme Klasse innerhalb der Methode
new Object() {
void sayHello() {
System.out.println(greeting); // Das ist erlaubt
// greeting = "Tschüss!"; // Das führt zu einem Fehler!
}
}.sayHello();
}
}
new OuterClass().greet();
}
Warum ist das so? Eine anonyme Klasse kann länger „leben“ als die Methode selbst, und wenn sie die Variable ändern könnte, würde das zu Problemen führen.
Kein Konstruktor. Da eine anonyme Klasse keinen Namen hat, kann sie auch keinen Konstruktor besitzen. Du kannst jedoch einen Initialisierungsblock verwenden, um Code bei der Objekterzeugung auszuführen:
Animal dog = new Animal() {
// Initialisierungsblock
{
System.out.println("Initialisierung der anonymen Klasse 🐶");
}
@Override
void say() {
System.out.println("Wau-wau!");
}
};
Einschränkungen. Anonyme Klassen können keine statischen Felder (außer Konstanten) oder Methoden deklarieren. Sie werden immer als Teil eines anderen Objekts erzeugt, daher können sie nicht static, public, protected oder private sein.
4. Nützliche Feinheiten
Wann anonyme Klassen verwenden
- Du musst eine Klasse (oder ein Interface) genau einmal erweitern/implementieren.
- Die Implementierung ist klein – 1–2 Methoden, nicht mehr als ein paar Dutzend Zeilen.
- Die Klasse wird nur an einer Stelle benötigt und es ergibt keinen Sinn, ihr einen Namen zu geben.
- Du möchtest vermeiden, das Paket mit vielen kleinen Einmal-Klassen zu überladen.
Typische Szenarien:
- Event-Handler (GUI, Swing, Android usw.).
- Übergabe von Callbacks an Methoden.
- Schnelle Implementierung von Comparatoren für das Sortieren von Collections.
- Temporär veränderte Objekte (z. B. für Tests).
5. Interaktion mit der äußeren Klasse
Wenn eine anonyme Klasse in einer nichtstatischen Methode oder in einem Block der äußeren Klasse deklariert ist, kann sie auf die Felder und Methoden dieser äußeren Klasse zugreifen (auch auf private!).
public class Outer {
private String secret = "Geheimer Text";
// Basisklasse
class Printer {
public void print() {
System.out.println("Normale Ausgabe");
}
}
public void revealSecret() {
Printer p = new Printer() {
@Override
public void print() {
System.out.println("Zugriff auf privates Feld: " + secret);
}
};
p.print();
}
public static void main(String[] args) {
new Outer().revealSecret();
}
}
6. Typische Fehler im Umgang mit anonymen Klassen
Fehler Nr. 1: Versuch, eine Variable der umgebenden Methode zu ändern.
Wenn du eine Variable außerhalb der anonymen Klasse deklariert hast und versuchst, sie nach der Verwendung innerhalb der anonymen Klasse zu ändern, meldet der Compiler einen Fehler. Die Variable muss final oder effectively final sein (nach der Initialisierung unverändert bleiben).
Fehler Nr. 2: Eine zu große anonyme Klasse.
Wenn die anonyme Klasse auf Dutzende Zeilen anwächst und mehrere Methoden enthält, ist das ein Signal, sie in eine separate benannte Klasse auszulagern. Andernfalls wird der Code unlesbar.
Fehler Nr. 3: Versuch, statische Methoden oder Felder zu verwenden.
In einer anonymen Klasse dürfen keine statischen Methoden oder Felder (außer Konstanten) deklariert werden. Wenn du sie wirklich brauchst, ist das ein Anlass, eine normale geschachtelte Klasse zu verwenden.
Fehler Nr. 4: Geltungsbereich vergessen.
Eine anonyme Klasse ist nur an der Stelle sichtbar, an der sie deklariert wird, und hat keinen Namen. Wenn du sie mehrfach verwenden musst, deklariere eine normale Klasse.
GO TO FULL VERSION