W3docs

Anonyme innere Klassen in Java

Einmalige Implementierungen von Interfaces und abstrakten Klassen in Java mit anonymen inneren Klassen erstellen.

Eine anonyme Klasse ist eine einmalig verwendete Unterklasse oder Interface-Implementierung, die in einem einzigen Ausdruck definiert und instanziiert wird – ohne Namen, ohne separate Datei, ohne Klassenheader. Sie wurden in Java eingesetzt, um Callbacks, Listener und kleine Adapter zu realisieren, bevor Lambda-Ausdrücke in Java 8 eingeführt wurden. Lambdas haben die meisten ihrer Anwendungsfälle übernommen, doch anonyme Klassen sind nach wie vor gültig, in bestimmten Situationen nützlich und tauchen in älterem Code häufig auf.

Die Syntax

Die Form lautet new SomeType() { ... body ... }. Der Rumpf ist die Klassendefinition; der umgebende Ausdruck erstellt gleichzeitig eine Instanz:

Runnable r = new Runnable() {
  @Override
  public void run() {
    System.out.println("hi");
  }
};
r.run();

Diese einzelne Anweisung (1) definiert eine neue Klasse, die Runnable implementiert, (2) erstellt eine Instanz davon und (3) weist die Instanz r zu. Die Klasse hat in Ihrem Quellcode keinen Namen; der Compiler vergibt einen generierten Namen wie Outer$1 in der .class-Datei.

Das Gleiche funktioniert mit einer abstrakten Klasse:

Shape s = new Shape() {
  @Override double area() { return 42; }
};

…oder sogar mit einer konkreten Klasse, wenn Sie eine ihrer Methoden inline überschreiben möchten:

ArrayList<String> chatty = new ArrayList<>() {
  @Override
  public boolean add(String s) {
    System.out.println("added " + s);
    return super.add(s);
  }
};

Wann man sie verwendet

Der klassische Fall: ein Callback, dessen Implementierung klein ist und genau an einer Stelle genutzt wird:

button.addClickListener(new ClickListener() {
  @Override
  public void onClick() {
    System.out.println("clicked");
  }
});

In modernem Java würde man das üblicherweise als Lambda schreiben:

button.addClickListener(() -> System.out.println("clicked"));

…und das Lambda ist kürzer, leichter zu lesen und hält keine Referenz auf die äußere Klasse. Wann ist die anonyme Form also noch sinnvoll?

  • Mehrere Methoden. Ein Lambda implementiert genau eine abstrakte Methode. Wenn Sie zwei Methoden einer abstrakten Klasse überschreiben oder zwei eines Interface implementieren müssen, ist nur eine anonyme Klasse geeignet.
  • Unterklasse einer konkreten Klasse. Lambdas zielen ausschließlich auf funktionale Interfaces ab. Um eine Methode von ArrayList, HashMap oder einer eigenen konkreten Klasse spontan zu überschreiben, benötigen Sie eine anonyme Klasse.
  • Aufruf von super.method(...) in der Überschreibung. Lambdas kennen kein super. Anonyme Klassen schon.
  • Initialisierungsblöcke. Anonyme Klassen können Instanz-Initialisierungsblöcke ({ ... }) besitzen; Lambdas nicht.

In der Praxis sind das nur wenige Fälle. Die meisten modernen Callbacks verwenden Lambdas.

Variablen erfassen

Eine anonyme Klasse, die innerhalb einer Methode deklariert wird, kann auf lokale Variablen der umgebenden Methode zugreifen – jedoch nur, wenn sie final oder effektiv final sind (nach ihrer Initialisierung nie neu zugewiesen werden):

void schedule() {
  String msg = "hello";
  Runnable r = new Runnable() {
    @Override public void run() {
      System.out.println(msg);     // captures msg
    }
  };
  r.run();
  // msg = "bye";          // would make msg no longer effectively final → ERROR above
}

Dieselbe Regel gilt für Lambdas – sie ist eine Eigenschaft des umgebenden Codes, nicht der syntaktischen Form. Der Grund: Der erfasste Wert wird in die synthetischen Felder der Klasse kopiert; könnte sich msg später ändern, wäre die kopierte Version stillschweigend veraltet.

Sie haben ein äußeres this

Wie innere Klassen tragen anonyme Klassen, die in einem nicht-statischen Kontext deklariert werden, eine Referenz auf die einschließende Instanz. Das bedeutet, dass this innerhalb der anonymen Klasse auf die anonyme Instanz verweist, nicht auf die äußere. Um die äußere Instanz zu erreichen, qualifizieren Sie sie mit Outer.this:

public class Server {
  String name = "outer";

  Runnable handler() {
    return new Runnable() {
      String name = "inner";
      public void run() {
        System.out.println(name);            // "inner"
        System.out.println(Server.this.name);// "outer"
      }
    };
  }
}

Und wie bei inneren Klassen gilt: Das Zurückgeben einer anonymen Instanz an langlebigen Code hält die äußere Instanz am Leben.

Einschränkungen

  • Eine anonyme Klasse kann eine Superklasse erweitern oder ein Interface implementieren – nicht beides, und nicht mehr als eines davon.
  • Sie kann keinen eigenen Konstruktor haben – es gibt keinen Namen für den Konstruktor. Für die Einrichtung kann ein Instanz-Initialisierungsblock ({ ... }) verwendet werden.
  • Sie kann nicht static sein.

Vergleich mit Lambdas – der Vergleich im Code

Dieselbe Aufgabe, zwei Wege:

// Anonymous class — older style
Comparator<String> byLength = new Comparator<String>() {
  @Override
  public int compare(String a, String b) {
    return Integer.compare(a.length(), b.length());
  }
};

// Lambda — modern equivalent
Comparator<String> byLength = (a, b) -> Integer.compare(a.length(), b.length());

Beide erzeugen einen Comparator<String>. Das Lambda ist kürzer und hat leicht unterschiedliche this/Scoping-Semantik (keine Referenz auf die äußere Klasse in Instanzkontexten; this innerhalb eines Lambdas ist die einschließende Instanz, kein neues Objekt). Wenn beide Formen für Ihren Anwendungsfall geeignet sind, bevorzugen Sie das Lambda.

Ein ausgearbeitetes Beispiel

java— editable, runs on the server

Was kommt als Nächstes

Die verbleibende Variante verschachtelter Klassen ist die lokale Klasse – eine Klasse, die innerhalb eines Methodenrumpfs deklariert wird, einen echten Namen, aber einen sehr begrenzten Gültigkeitsbereich hat. Sie überschneiden sich mit anonymen Klassen (und mit Lambdas), sind aber gelegentlich die sauberste Wahl. Weiter zu lokalen Klassen.

Übungen

Übung
Welche Aufgabe erfordert in modernem Java noch eine anonyme Klasse anstelle eines Lambdas?
Welche Aufgabe erfordert in modernem Java noch eine anonyme Klasse anstelle eines Lambdas?
Was this page helpful?