Anonyme Klassen in Java

1. Einleitung

In diesem Tutorial werden anonyme Klassen in Java betrachtet.

Wir werden beschreiben, wie wir Instanzen davon deklarieren und erstellen können. Wir werden auch kurz auf ihre Eigenschaften und Einschränkungen eingehen.

2. Anonyme Klassenerklärung

Anonyme Klassen sind innere Klassen ohne Namen. Da sie keinen Namen haben, können wir sie nicht verwenden, um Instanzen anonymer Klassen zu erstellen. Daher müssen wir an der Verwendungsstelle anonyme Klassen in einem einzigen Ausdruck deklarieren und instanziieren.

Wir können entweder eine vorhandene Klasse erweitern oder eine Schnittstelle implementieren.

2.1. Eine Klasse erweitern

Wenn wir eine anonyme Klasse von einer vorhandenen instanziieren, verwenden wir die folgende Syntax:

In den Klammern geben wir die Parameter an, die vom Konstruktor der Klasse, die wir erweitern, benötigt werden:

new Book("Design Patterns") { @Override public String description() { return "Famous GoF book."; } }

Wenn der übergeordnete Klassenkonstruktor keine Argumente akzeptiert, sollten wir die Klammern natürlich leer lassen.

2.2. Implementieren Sie eine Schnittstelle

Wir können eine anonyme Klasse auch über eine Schnittstelle instanziieren:

Offensichtlich haben Javas Schnittstellen keine Konstruktoren, daher bleiben die Klammern immer leer. Dies ist die einzige Möglichkeit, die Methoden der Schnittstelle zu implementieren:

new Runnable() { @Override public void run() { ... } }

Sobald wir eine anonyme Klasse instanziiert haben, können wir diese Instanz einer Variablen zuweisen, um später darauf verweisen zu können.

Wir können dies mit der Standardsyntax für Java-Ausdrücke tun:

Runnable action = new Runnable() { @Override public void run() { ... } };

Wie bereits erwähnt, ist eine anonyme Klassendeklaration ein Ausdruck und muss daher Teil einer Anweisung sein . Dies erklärt, warum wir am Ende der Anweisung ein Semikolon eingefügt haben.

Natürlich können wir vermeiden, die Instanz einer Variablen zuzuweisen, wenn wir diese Instanz inline erstellen:

List actions = new ArrayList(); actions.add(new Runnable() { @Override public void run() { ... } });

Wir sollten diese Syntax mit großer Sorgfalt verwenden, da sie leicht die Lesbarkeit des Codes beeinträchtigen kann, insbesondere wenn die Implementierung der run () -Methode viel Platz beansprucht.

3. Anonyme Klasseneigenschaften

Es gibt bestimmte Besonderheiten bei der Verwendung anonymer Klassen in Bezug auf übliche Klassen der obersten Ebene. Hier gehen wir kurz auf die praktischsten Fragen ein. Die genauesten und aktuellsten Informationen finden Sie immer in der Java-Sprachspezifikation.

3.1. Konstrukteur

Die Syntax anonymer Klassen erlaubt es uns nicht, sie dazu zu bringen, mehrere Schnittstellen zu implementieren. Während der Erstellung kann genau eine Instanz einer anonymen Klasse vorhanden sein . Daher können sie niemals abstrakt sein. Da sie keinen Namen haben, können wir sie nicht erweitern. Aus dem gleichen Grund können anonyme Klassen keine explizit deklarierten Konstruktoren haben.

Tatsächlich ist das Fehlen eines Konstruktors aus folgenden Gründen für uns kein Problem:

  1. Wir erstellen anonyme Klasseninstanzen im selben Moment, in dem wir sie deklarieren
  2. Von anonymen Klasseninstanzen aus können wir auf lokale Variablen zugreifen und die Mitglieder der Klasse einschließen

3.2. Statische Mitglieder

Anonyme Klassen können keine statischen Elemente haben, außer solchen, die konstant sind.

Dies wird beispielsweise nicht kompiliert:

new Runnable() { static final int x = 0; static int y = 0; // compilation error! @Override public void run() {...} };

Stattdessen wird der folgende Fehler angezeigt:

The field y cannot be declared static in a non-static inner type, unless initialized with a constant expression

3.3. Umfang der Variablen

Anonyme Klassen erfassen lokale Variablen, die sich im Bereich des Blocks befinden, in dem wir die Klasse deklariert haben:

int count = 1; Runnable action = new Runnable() { @Override public void run() { System.out.println("Runnable with captured variables: " + count); } }; 

Wie wir sehen, werden die Anzahl und die Aktion der lokalen Variablen im selben Block definiert. Aus diesem Grund können wir innerhalb der Klassendeklaration auf count zugreifen .

Beachten Sie, dass lokale Variablen effektiv endgültig sein müssen, um sie verwenden zu können. Seit JDK 8 ist es nicht mehr erforderlich, Variablen mit dem Schlüsselwort final zu deklarieren . Diese Variablen müssen jedoch endgültig sein . Andernfalls wird ein Kompilierungsfehler angezeigt:

[ERROR] local variables referenced from an inner class must be final or effectively final

Damit der Compiler entscheidet, dass eine Variable im Code tatsächlich unveränderlich ist, sollte es nur eine Stelle geben, an der wir ihr einen Wert zuweisen. Weitere Informationen zu effektiv endgültigen Variablen finden Sie möglicherweise in unserem Artikel „Warum müssen in Lambdas verwendete lokale Variablen endgültig oder effektiv endgültig sein?“.

Erwähnen wir nur, dass eine anonyme Klasse wie jede innere Klasse auf alle Mitglieder ihrer einschließenden Klasse zugreifen kann .

4. Anwendungsfälle für anonyme Klassen

Es kann eine Vielzahl von Anwendungen für anonyme Klassen geben. Lassen Sie uns einige mögliche Anwendungsfälle untersuchen.

4.1. Klassenhierarchie und Kapselung

We should use inner classes in general use cases and anonymous ones in very specific ones in order to achieve a cleaner hierarchy of classes in our application. When using inner classes, we may achieve a finer encapsulation of the enclosing class's data. If we define the inner class functionality in a top-level class, then the enclosing class should have public or package visibility of some of its members. Naturally, there are situations when it is not very appreciated or even accepted.

4.2. Cleaner Project Structure

We usually use anonymous classes when we have to modify on the fly the implementation of methods of some classes. In this case, we can avoid adding new *.java files to the project in order to define top-level classes. This is especially true if that top-level class would be used just one time.

4.3. UI Event Listeners

In applications with a graphical interface, the most common use case of anonymous classes is to create various event listeners. For example, in the following snippet:

button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ... } }

we create an instance of an anonymous class that implements interface ActionListener. Its actionPerformed method gets triggered when a user clicks the button.

Since Java 8, lambda expressions seem to be a more preferred way though.

5. General Picture

Anonyme Klassen, die wir oben betrachtet haben, sind nur ein besonderer Fall von verschachtelten Klassen. Im Allgemeinen ist eine verschachtelte Klasse eine Klasse, die in einer anderen Klasse oder Schnittstelle deklariert ist :

Wenn wir uns das Diagramm ansehen, sehen wir, dass anonyme Klassen zusammen mit lokalen und nicht statischen Mitgliedsklassen die sogenannten inneren Klassen bilden . Zusammen mit statischen Elementklassen bilden sie die verschachtelten Klassen.

6. Fazit

In diesem Artikel haben wir verschiedene Aspekte von anonymen Java-Klassen betrachtet. Wir haben auch eine allgemeine Hierarchie verschachtelter Klassen beschrieben.

Wie immer ist der vollständige Code in unserem GitHub-Repository verfügbar.