Anonymous Classes
Kurz: Eine Klasse ohne eigenen Namen, die direkt an der Stelle definiert und sofort instanziiert wird — meist um ein Interface oder eine abstrakte Klasse einmalig zu implementieren.
Genauer: Häufig genutzt für einmalige Event-Handler oder Callback-Implementierungen, bei denen sich eine separate, benannte Klasse nicht lohnt. Seit Java 8 übernehmen Lambda-Ausdrücke diese Rolle für funktionale Interfaces (Interfaces mit genau einer Methode) meist kompakter.
Runnable r = new Runnable() {
public void run() { System.out.println("läuft"); }
};Im Detail
Eine anonyme Klasse erzeugt beim Kompilieren tatsächlich eine eigene .class-Datei (z. B. Aussenklasse$1.class), auch wenn sie im Quellcode keinen eigenen Namen trägt — der Compiler vergibt intern einen Zähler-basierten Namen. Im Gegensatz zu einem Lambda-Ausdruck kann eine anonyme Klasse mehr als eine Methode implementieren und darf zusätzlich eigene Felder und einen eigenen Konstruktor-ähnlichen Instanzinitialisierer haben:
interface Formatter {
String format(int wert);
}
// Anonyme Klasse mit eigenem Feld
Formatter waehrung = new Formatter() {
private final String symbol = "€";
@Override
public String format(int wert) {
return wert + " " + symbol;
}
};
System.out.println(waehrung.format(42)); // "42 €"Anonyme Klassen können auf final (oder faktisch finale, also nicht mehr veränderte) lokale Variablen der umgebenden Methode zugreifen — das nennt man ein Closure. Seit Java 8 ersetzt ein Lambda die anonyme Klasse fast immer dann, wenn nur EINE Methode implementiert wird (ein sogenanntes funktionales Interface wie Runnable oder Comparator) — kürzer und ohne den new Interface() { ... }-Rahmen. Für Interfaces mit mehreren Methoden oder wenn eigener Zustand gebraucht wird, bleibt die anonyme Klasse nötig.
Zugriff auf die umgebende Klasse
Anders als eine normale Klasse kann eine anonyme Klasse gleichzeitig auf Felder/Methoden der umgebenden (“äußeren”) Instanz UND auf finale lokale Variablen der Methode zugreifen, in der sie erzeugt wird:
class ZaehlerFabrik {
private int basis = 100;
Runnable erzeugeZaehler(int schritt) {
return new Runnable() {
public void run() {
// Zugriff sowohl auf "basis" (äußere Instanz)
// als auch "schritt" (finale lokale Variable)
System.out.println(basis + schritt);
}
};
}
}Häufiger Fallstrick: “effectively final”
Eine anonyme Klasse darf eine lokale Variable der umgebenden Methode nur lesen, wenn diese entweder explizit final ist oder “effectively final” (nach der Initialisierung nie mehr neu zugewiesen wird). Wird die Variable danach doch noch verändert, meldet der Compiler einen Fehler (“local variables referenced from an inner class must be final or effectively final”) — ein klassischer Stolperstein beim Umbau von Schleifen mit veränderlichen Zählervariablen zu Callback-Code.
Wann anonyme Klasse statt Lambda?
Ein Lambda kann NUR funktionale Interfaces (genau eine abstrakte Methode) implementieren und hat keinen eigenen this-Bezug (es referenziert das this der umgebenden Klasse). Eine anonyme Klasse ist nötig, wenn: mehrere Methoden überschrieben werden müssen, eigene zusätzliche Felder gebraucht werden, oder eine abstrakte KLASSE (statt eines Interfaces) implementiert werden soll — Lambdas funktionieren ausschließlich mit Interfaces.
Siehe auch: Inner Classes, Lambda, Interface