Anonymous Classes

Anonyme Klassen

Anonyme Klasse (anonymous class)

  • Klasse hat keinen Namen und kann daher nicht noch einmal referenziert werden
  • Deklaration einer Klasse und Erzeugung eines Objekts der Klasse innerhalb einer Methode
  • Außerhalb der Methode nicht sichtbar
  • Kann nur genau ein Interface implementieren oder genau eine Basisklasse ableiten, nicht beides gleichzeitig
class A {
    void methode() {
        Object o = new Object() { };
    }
}

Da anonyme innere Klassen keinen Namen haben, kann man keine Referenzen mit ihrem Typ definieren; die Referenzen haben immer den Typ der Klasse von der die anonyme Klasse abgeleitet wird oder des Interfaces, das von ihr implementiert wird. Instanziierung und Deklaration müssen zusammenfallen, da es später keine Möglichkeit gibt, mit new ein Objekt zu erzeugen – man kann den Namen der Klasse nicht angeben. Somit gibt es von einer anonymen inneren Klasse immer nur genau ein Objekt.

Wenn die anonyme Klasse in einer statischen Methode deklariert wird, kann sie nur noch auf statische Methoden und statische Felder der äußeren Klasse zugreifen, da sie dann nicht mehr im Kontext einer Instanz der äußeren Klasse existiert.

Aus diesem Grund ist es sinnlos, in anonymen inneren Klassen öffentliche Methoden zu definieren, die nicht bereits in der Oberklasse bzw. dem implementierten Interface vorhanden sind, da sie von außen über die Referenzen nicht erreichbar sind. Daher wird man im Allgemeinen nur Methoden der Superklasse überschreiben bzw. Methoden des Interfaces implementieren. Eigene öffentliche Methoden haben keinen Nutzen, weil für den Verwender nicht sichtbar.

Die Syntax zur Deklaration und gleichzeitigen Instanziierung einer anonymen Klasse ist:

Typ referenz = new Typ() {
  KLASSENDEFINITION
};

Als Typ kann nur entweder eine Basisklasse oder ein Interface angegeben werden. Beides gleichzeitig ist nicht möglich.

  • Zugriff auf alle Attribute und Methoden der äußeren Klasse (Ausnahme anonyme Klassen in statischen Methoden)
  • Zugriff auf alle final Variablen der Methode, in der sie deklariert wurde
  • Sichtbarkeit (public, private, protected oder default) kann nicht angegeben werden

Für die anonyme Klassen (die den lokalen Klassen sehr ähnlich sind) kann keine Sichtbarkeit angegeben werden, da sie die geringstmögliche Sichtbarkeit bereits haben: sie ist nur im Augenblick ihrer Deklaration sichtbar.

Die Argumentation zu den lokalen Variablen wurde bereits bei den lokalen Klassen geliefert (s. o.).

Beispiel: Anonyme Klassen

public class AnonymBeispiel {

    public static void main(String[] args) {

        Object o = new Object() {
            public String toString() {
                return "Anonym...";
            }
        };

        System.out.println(o);
    }
}
Anonym...

Die anonyme Klasse kann nicht referenziert werden. Daher fallen bei ihr die Deklaration und die Objekterzeugung zusammen, da man sich später nicht mehr auf die Klasse beziehen kann.

Beispiel: Ausbruch des Objekts

public class Escape {

    public static Object escape() {

        return new Object() {
            public String toString() {
                return "Ich bin geflohen";
            }
        };
    }
}


Object o = Escape.escape();
System.out.println(o.toString());
Ich bin geflohen

Obwohl die anonyme innere Klasse nicht referenzierbar ist, können Objekte dieser Klasse die Methode verlassen und zurückgegeben werden. Der dynamische Typ ist dann für den Außenstehenden nicht verwendbar (da nicht sichtbar), sondern er kann das Objekt nur über eine Referenz von einem Typ manipulieren, der in der Vererbungshierarchie über der anonymen Klasse steht und der ihm bekannt ist – im vorliegenden Beispiel Object.

Beispiel: Anonyme Klassen

public class Outer {

    public void sortSpecial(String[] strings) {

        Comparator<String> c = new Comparator<String>() {
            public int compare(String s1, String s2) {
                int result = s1.toUpperCase().compareTo(s2.toUpperCase());
                return result;
            }
        };

        Arrays.sort(strings, c);
    }
}

In diesem Beispiel wird eine anonyme Klasse dazu verwendet, einen Comparator, der Groß- und Kleinschreibung ignoriert, für die Sortierung eines String-Arrays zu verwenden.

Der hier dargestellte Code ist semantisch äquivalent mit dem folgenden (mit dem Unterschied, dass die Klasse hier einen Namen DontCare hat).

public class Outer2 {

    public void sortSpecial(String[] strings) {

        class DontCare implements Comparator<String> {

            public int compare(String s1, String s2) {
                int result = s1.toUpperCase().compareTo(s2.toUpperCase());
                return result;
            }
        }

        Comparator<String> c = new DontCare();

        Arrays.sort(strings, c);
    }
}

Noch kürzer könnte man das Beispiel schreiben als:

public class Outer {

    public void sortSpecial(String[] strings) {

        Arrays.sort(strings, new Comparator<String>() {

            public int compare(String s1, String s2) {
                int result = s1.toUpperCase().compareTo(s2.toUpperCase());
                return result;
            }
        });
    }
}

Mit den sogenannten Lambdas, die später noch eingeführt werden, würde es sich sogar verkürzen lassen zu:

public class Outer {

    public void sortSpecial(String[] strings) {
        Arrays.sort(strings, (s1, s2) -> s1.toUpperCase().compareTo(s2.toUpperCase()));
    }
}
public class Outer {

    public void sortSpecial(String[] strings) {

        Arrays.sort(strings, new Comparator<String>() {

            public int compare(String s1, String s2) {
                int result = s1.toUpperCase().compareTo(s2.toUpperCase());
                return result;
            }
        });
    }
}

Beispiel: Anonymer Konstruktor

public class A {
    String s;

    public A(String s) {
        this.s = s;
    }

    public void methode() {
        System.out.println(s);
    }
}
import java.util.Date;

public class Anonymous {
    public static void main(String[] args) {
        final String welt = "Welt";

        A a = new A("Hallo") {
            Date date = new Date();
            { // Konstruktor der anonymen Klasse
                this.s = this.s.toUpperCase() + " "  + welt;
            }
            public void methode() {
                System.out.println(this.s + " am " + date);
            }
        };
        a.methode();
    }
}

Anonyme Klassen haben keinen Namen. Daher kann man keinen Konstruktor für sie angeben, denn der Konstruktor muss so heißen wie die Klasse. Es gibt aber die Möglichkeit mithilfe eines Exemplar-Initialisierungsblocks Initialisierungen für anonyme Klassen durchzuführen. Diese Blöcke können aber keine Parameter bekommen und keine Superklassenkonstruktoren explizit aufrufen.

Puzzler

import java.util.Date;

public class WasIstDennDas {

    public static void main(String[] args) {
        Date date = new Date() {{ setTime(400000000000L); }};
        System.out.println(date);
    }
}
Sat Sep 04 17:06:40 CEST 1982

Was man hier leicht übersehen kann, ist, dass gar kein Objekt vom Typ Date erzeugt wird, sondern eine anonyme Subklasse von Date. Im anonymen Konstruktor wird dann das Datum gesetzt, sodass nicht, wie man vielleicht erwarten würde das aktuelle Datum ausgegeben wird.

Callbacks mit anonymen Klassen

public interface Funktion {
    public abstract int apply(int o1, int o2);
}
public class Berechnung {

    public static int berechne(int input1, int input2, Funktion funktion) {
        int ergebnis = funktion.apply(input1, input2);

        return ergebnis;
    }
}
public class TestBerechnung {

    public static void main(String[] args) {

        int ergebnis = Berechnung.berechne(7, 3, new Funktion() {
            public int apply(int o1, int o2) {
                return o1 - o2;
            }
        });

        ergebnis = Berechnung.berechne(5, 6,  new Funktion() {
            public int apply(int o1, int o2) {
                return o1 + o2;
            }
        });
    }
}

Wir greifen hier noch einmal das Beispiel aus dem Kapitel „Objektorientierung“ auf und verwenden anonyme Klassen, um das Interface Funktion für Addition und Multiplikation zu implementieren.

Die Verwendung als Callback-Funktionen ist der Hauptanwendungszweck von anonymen inneren Klassen.


Copyright © 2025 Thomas Smits