Wie werden anonyme innere Klassen in Java verwendet?


309

Was ist die Verwendung von anonymen Klassen in Java? Können wir sagen, dass die Verwendung einer anonymen Klasse einer der Vorteile von Java ist?


67
Es ist weniger ein Vorteil von Java als vielmehr eine Möglichkeit, das Fehlen von Schließungen in Java zu umgehen.
Eric Wilson

4
Java 8 hat auch Lambda-Ausdrücke eingeführt. Überprüfen Sie die Antwort: stackoverflow.com/questions/355167/…
akhil_mittal

Antworten:


369

Mit einer "anonymen Klasse" meine ich anonyme innere Klasse .

Eine anonyme innere Klasse kann nützlich sein, wenn Sie eine Instanz eines Objekts mit bestimmten "Extras" wie z. B. überschreibenden Methoden erstellen, ohne eine Klasse tatsächlich unterordnen zu müssen.

Ich neige dazu, es als Verknüpfung zum Anhängen eines Ereignis-Listeners zu verwenden:

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        // do something
    }
});

Die Verwendung dieser Methode beschleunigt das Codieren etwas, da ich keine zusätzliche Klasse erstellen muss, die implementiert wird ActionListener Ich kann nur eine anonyme innere Klasse instanziieren, ohne tatsächlich eine separate Klasse zu erstellen.

Ich benutze diese Technik nur für "schnelle und schmutzige" Aufgaben, bei denen es sich unnötig anfühlt, eine ganze Klasse zu bilden. Mehrere anonyme innere Klassen, die genau dasselbe tun, sollten in eine tatsächliche Klasse umgestaltet werden, sei es eine innere Klasse oder eine separate Klasse.


5
Oder Sie können doppelte anonyme innere Klassen mit der anonymen inneren Klasse (und möglicherweise einem anderen duplizierten Code) in eine Methode umgestalten.
Tom Hawtin - Tackline

3
Tolle Antwort, aber eine kurze Frage. Bedeutet dies, dass Java ohne anonyme innere Klassen leben kann und eine zusätzliche Option zur Auswahl darstellt?
RealPK

5
Sehr gut erklärt, auch wenn es schwierig ist, würde ich jedem empfehlen, der dies liest, um nachzuschauen, was Java 8- und Lambda-Ausdrücke tun können, um das Codieren schneller und lesbarer zu machen.
Pievis

2
@ user2190639 Genau, kann nicht besser mit Lambda in Java8 fragen
bonCodigo

3
warum hast du gesagt overloading methodsund nicht overriding methods?
Tarun

73

Anonyme innere Klassen sind effektiv Schließungen, sodass sie zum Emulieren von Lambda-Ausdrücken oder "Delegaten" verwendet werden können. Nehmen Sie zum Beispiel diese Schnittstelle:

public interface F<A, B> {
   B f(A a);
}

Sie können dies anonym verwenden, um eine erstklassige Funktion in Java zu erstellen . Angenommen, Sie haben die folgende Methode, die die erste Zahl zurückgibt, die größer als i in der angegebenen Liste ist, oder i, wenn keine Zahl größer ist:

public static int larger(final List<Integer> ns, final int i) {
  for (Integer n : ns)
     if (n > i)
        return n;
  return i;
}

Und dann haben Sie eine andere Methode, die die erste Zahl zurückgibt, die kleiner als i in der angegebenen Liste ist, oder i, wenn keine Zahl kleiner ist:

public static int smaller(final List<Integer> ns, final int i) {
   for (Integer n : ns)
      if (n < i)
         return n;
   return i;
}

Diese Methoden sind nahezu identisch. Mit dem erstklassigen Funktionstyp F können wir diese wie folgt in eine Methode umschreiben:

public static <T> T firstMatch(final List<T> ts, final F<T, Boolean> f, T z) {
   for (T t : ts)
      if (f.f(t))
         return t;
   return z;
}

Sie können eine anonyme Klasse verwenden, um die firstMatch-Methode zu verwenden:

F<Integer, Boolean> greaterThanTen = new F<Integer, Boolean> {
   Boolean f(final Integer n) {
      return n > 10;
   }
};
int moreThanMyFingersCanCount = firstMatch(xs, greaterThanTen, x);

Dies ist ein wirklich erfundenes Beispiel, aber es ist leicht zu erkennen, dass es eine ziemlich nützliche Funktion ist, Funktionen so weitergeben zu können, als wären sie Werte. Siehe "Kann Ihre Programmiersprache dies tun" von Joel selbst.

Eine schöne Bibliothek zum Programmieren von Java in diesem Stil: Functional Java.


20
Leider überwiegt die Ausführlichkeit der funktionalen Programmierung in Java, IMHO, die Vorteile - einer der auffälligsten Punkte der funktionalen Programmierung ist, dass sie dazu neigt, die Codegröße zu reduzieren und das Lesen und Ändern zu vereinfachen. Aber funktionales Java scheint das überhaupt nicht zu tun.
Chii

27
All die Verständlichkeit der funktionalen Programmierung mit der Knappheit von Java!
Adam Jaskiewicz

3
Nach meiner Erfahrung wird der funktionale Stil in Java mit Ausführlichkeit im Voraus bezahlt, aber auf lange Sicht führt er zu Kürze. Zum Beispiel ist myList.map (f) wesentlich weniger ausführlich als die entsprechende for-Schleife.
Apocalisp

2
Scala , eine funktionale Programmiersprache, läuft angeblich gut in der JVM und kann eine Option für funktionale Programmierszenarien sein.
Darrell Teague

51

Die anonyme innere Klasse wird in folgendem Szenario verwendet:

1.) Zum Überschreiben (Unterklassifizierung): Wenn die Klassendefinition nur im aktuellen Fall verwendet werden kann:

class A{
   public void methodA() {
      System.out.println("methodA");
    }
}
class B{
    A a = new A() {
     public void methodA() {
        System.out.println("anonymous methodA");
     }
   };
}

2.) Zum Implementieren einer Schnittstelle Wenn die Implementierung der Schnittstelle nur für den aktuellen Fall erforderlich ist:

interface interfaceA{
   public void methodA();
}
class B{
   interfaceA a = new interfaceA() {
     public void methodA() {
        System.out.println("anonymous methodA implementer");
     }
   };
}

3.) Argument definiert Anonyme innere Klasse:

 interface Foo {
   void methodFoo();
 }
 class B{
  void do(Foo f) { }
}

class A{
   void methodA() {
     B b = new B();
     b.do(new Foo() {
       public void methodFoo() {
         System.out.println("methodFoo");
       } 
     });
   } 
 } 

8
Tolle Antwort, sieht aus wie 3.) ist das Muster für Event-Listener
xdl

47

Ich benutze sie manchmal als Syntax-Hack für die Map-Instanziierung:

Map map = new HashMap() {{
   put("key", "value");
}};

vs.

Map map = new HashMap();
map.put("key", "value");

Dies spart Redundanz, wenn viele put-Anweisungen ausgeführt werden. Ich habe jedoch auch Probleme damit, wenn die äußere Klasse per Remoting serialisiert werden muss.


56
Der erste Satz von Klammern ist die anonyme innere Klasse (Unterklasse HashMap). Der zweite Satz von geschweiften Klammern ist ein Instanzinitialisierer (und kein statischer), der dann die Werte in Ihrer HashMap-Unterklasse festlegt. +1 für die Erwähnung, -1 für die Nichtschreibung für die Noobs. ;-D
Spencer Kormos

4
Lesen Sie mehr über die Doppel Klammer Syntax hier .
Martin Andersson


18

Sie werden häufig als ausführliche Form des Rückrufs verwendet.

Ich nehme an, Sie könnten sagen, dass sie ein Vorteil sind, wenn Sie sie nicht haben und jedes Mal eine benannte Klasse erstellen müssen, aber ähnliche Konzepte werden in anderen Sprachen (als Abschlüsse oder Blöcke) viel besser implementiert.

Hier ist ein Swing-Beispiel

myButton.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
        // do stuff here...
    }
});

Obwohl es immer noch chaotisch ausführlich ist, ist es viel besser, als Sie zu zwingen, eine benannte Klasse für jeden wegwerfbaren Hörer wie diesen zu definieren (obwohl dies je nach Situation und Wiederverwendung immer noch der bessere Ansatz sein kann).


1
Wolltest du knapp sagen? Wenn es ausführlich wäre, würde der Rückruf separat bleiben, was ihn etwas größer und damit ausführlich macht. Wenn Sie sagen, dass dies immer noch ausführlich ist, was wäre dann eine knappe Form?
user3081519

1
@ user3081519, so etwas wie myButton.addActionListener(e -> { /* do stuff here */})oder myButton.addActionListener(stuff)wäre terser.
Samuel Edwin Ward

8

Sie verwenden es in Situationen, in denen Sie eine Klasse für einen bestimmten Zweck in einer anderen Funktion erstellen müssen, z. B. als Listener, als ausführbare Datei (um einen Thread zu erzeugen) usw.

Die Idee ist, dass Sie sie aus dem Code einer Funktion heraus aufrufen, sodass Sie sie nie an anderer Stelle referenzieren, sodass Sie sie nicht benennen müssen. Der Compiler zählt sie nur auf.

Sie sind im Wesentlichen syntaktischer Zucker und sollten im Allgemeinen an einen anderen Ort gebracht werden, wenn sie größer werden.

Ich bin mir nicht sicher, ob dies einer der Vorteile von Java ist. Wenn Sie sie jedoch verwenden (und wir alle verwenden sie leider häufig), können Sie argumentieren, dass es sich um einen handelt.


6

Richtlinien für anonyme Klassen.

  1. Die anonyme Klasse wird gleichzeitig deklariert und initialisiert.

  2. Die anonyme Klasse muss auf eine und nur eine Klasse bzw. Schnittstelle erweitert oder implementiert werden.

  3. Da die anonymouse-Klasse keinen Namen hat, kann sie nur einmal verwendet werden.

z.B:

button.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent arg0) {
        // TODO Auto-generated method stub

    }
});

Zu # 3: Nicht ganz richtig. Sie können mehrere Instanzen einer anonymen Klasse mit Reflexion erwerben, z ref.getClass().newInstance().
icza

Regeln beantworten die Frage nicht.
Marquis von Lorne

5

Ja, anonyme innere Klassen sind definitiv einer der Vorteile von Java.

Mit einer anonymen inneren Klasse haben Sie Zugriff auf endgültige und Mitgliedsvariablen der umgebenden Klasse, was für Hörer usw. nützlich ist.

Ein großer Vorteil ist jedoch, dass der innere Klassencode, der eng mit der umgebenden Klasse / Methode / dem umgebenden Block verbunden ist (zumindest sein sollte), einen bestimmten Kontext hat (die umgebende Klasse, Methode und den umgebenden Block).


1
Der Zugang zur umliegenden Klasse ist sehr wichtig! Ich denke, dies ist in vielen Fällen der Grund, warum anonyme Klassen verwendet werden, da sie die nicht öffentlichen Attribute, Methoden und lokalen Variablen der umgebenden Klasse / Methode benötigen / verwenden, die äußerlich (wenn eine separate Klasse verwendet würde) erforderlich wären bestanden oder veröffentlicht werden.
icza

5
new Thread() {
        public void run() {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                System.out.println("Exception message: " + e.getMessage());
                System.out.println("Exception cause: " + e.getCause());
            }
        }
    }.start();

Dies ist auch eines der Beispiele für anonyme innere Typen, die Thread verwenden


3

Ich benutze anonyme Objekte zum Aufrufen neuer Threads.

new Thread(new Runnable() {
    public void run() {
        // you code
    }
}).start();

3

Eine innere Klasse ist einer Instanz der äußeren Klasse zugeordnet, und es gibt zwei spezielle Arten: Lokale Klasse und Anonyme Klasse . Eine anonyme Klasse ermöglicht es uns, eine Klasse gleichzeitig zu deklarieren und zu instanziieren, wodurch der Code präzise wird. Wir verwenden sie, wenn wir eine lokale Klasse nur einmal benötigen, da sie keinen Namen haben.

Betrachten Sie das Beispiel aus doc, in dem wir eine PersonKlasse haben:

public class Person {

    public enum Sex {
        MALE, FEMALE
    }

    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;

    public int getAge() {
        // ...
    }

    public void printPerson() {
        // ...
    }
}

und wir haben eine Methode zum Drucken von Mitgliedern, die Suchkriterien entsprechen:

public static void printPersons(
    List<Person> roster, CheckPerson tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

Wo CheckPersonist eine Schnittstelle wie:

interface CheckPerson {
    boolean test(Person p);
}

Jetzt können wir eine anonyme Klasse verwenden, die diese Schnittstelle implementiert, um Suchkriterien wie folgt anzugeben:

printPersons(
    roster,
    new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
        }
    }
);

Hier ist die Oberfläche sehr einfach und die Syntax der anonymen Klasse scheint unhandlich und unklar.

Java 8 hat einen Begriff Functional Interface eingeführt, der eine Schnittstelle mit nur einer abstrakten Methode ist, daher können wir sagen, dass CheckPersones sich um eine funktionale Schnittstelle handelt. Wir können den Lambda-Ausdruck verwenden , mit dem wir die Funktion als Methodenargument übergeben können als:

printPersons(
                roster,
                (Person p) -> p.getGender() == Person.Sex.MALE
                        && p.getAge() >= 18
                        && p.getAge() <= 25
        );

Wir können Predicateanstelle der Schnittstelle eine Standardfunktionsschnittstelle verwenden CheckPerson, wodurch die erforderliche Codemenge weiter reduziert wird.


2

Eine anonyme innere Klasse kann nützlich sein, wenn unterschiedliche Implementierungen für unterschiedliche Objekte angegeben werden. Sollte aber sehr sparsam verwendet werden, da dies zu Problemen bei der Programmlesbarkeit führt.


1

Eine der Hauptverwendungen anonymer Klassen bei der Klassenfinalisierung, die als Finalizer Guardian bezeichnet wird . In der Java-Welt sollte die Verwendung der Finalize-Methoden vermieden werden, bis Sie sie wirklich benötigen. Sie müssen sich daran erinnern, dass Sie beim Überschreiben der Finalisierungsmethode für Unterklassen immer auch aufrufen sollten super.finalize(), da die Finalisierungsmethode der Superklasse nicht automatisch aufgerufen wird und Sie Probleme mit Speicherverlusten haben können.

In Anbetracht der oben genannten Tatsache können Sie einfach die anonymen Klassen wie folgt verwenden:

public class HeavyClass{
    private final Object finalizerGuardian = new Object() {
        @Override
        protected void finalize() throws Throwable{
            //Finalize outer HeavyClass object
        }
    };
}

Mit dieser Technik haben Sie sich und Ihre anderen Entwickler entlastet, super.finalize()jede Unterklasse HeavyClassder zu finalisierenden Methode aufzurufen .


1

Sie können auf diese Weise eine anonyme Klasse verwenden

TreeSet treeSetObj = new TreeSet(new Comparator()
{
    public int compare(String i1,String i2)
    {
        return i2.compareTo(i1);
    }
});

1

Hier scheint niemand erwähnt zu sein, aber Sie können auch eine anonyme Klasse verwenden, um generische Typargumente zu speichern (die normalerweise aufgrund von Typlöschung verloren gehen) :

public abstract class TypeHolder<T> {
    private final Type type;

    public TypeReference() {
        // you may do do additional sanity checks here
        final Type superClass = getClass().getGenericSuperclass();
        this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public final Type getType() {
        return this.type;
    }
}

Wenn Sie diese Klasse anonym instanziieren

TypeHolder<List<String>, Map<Ineger, Long>> holder = 
    new TypeHolder<List<String>, Map<Ineger, Long>>() {};

dann so holder Instanz eine nicht gelöschte Definition des übergebenen Typs.

Verwendung

Dies ist sehr praktisch, um Validatoren / Deserialisatoren zu erstellen. Sie können auch einen generischen Typ mit Reflexion instanziieren (wenn Sie also jemals einen new T()parametrisierten Typ verwenden wollten, sind Sie herzlich willkommen!) .

Nachteile / Einschränkungen

  1. Sie sollten generische Parameter explizit übergeben. Andernfalls kommt es zum Verlust von Typparametern
  2. Jede Instanziierung kostet Sie zusätzliche Klasse, die vom Compiler generiert wird, was zu Verschmutzung des Klassenpfads / Aufblähen des Glases führt

1

Der beste Weg, um Code zu optimieren. Wir können auch eine überschreibende Methode einer Klasse oder Schnittstelle verwenden.

import java.util.Scanner;
abstract class AnonymousInner {
    abstract void sum();
}

class AnonymousInnerMain {
    public static void main(String []k){
        Scanner sn = new Scanner(System.in);
        System.out.println("Enter two vlaues");
            int a= Integer.parseInt(sn.nextLine());
            int b= Integer.parseInt(sn.nextLine()); 
        AnonymousInner ac = new AnonymousInner(){
            void sum(){
                int c= a+b;
                System.out.println("Sum of two number is: "+c);
            }
        };
        ac.sum();
    }

}

1

Eine anonyme innere Klasse wird verwendet, um ein Objekt zu erstellen, auf das nie wieder verwiesen wird. Es hat keinen Namen und wird in derselben Anweisung deklariert und erstellt. Dies wird verwendet, wenn Sie normalerweise die Variable eines Objekts verwenden würden. Sie ersetzen die Variable durch das newSchlüsselwort, einen Aufruf eines Konstruktors und die Klassendefinition in {und }.

Wenn Sie ein Thread-Programm in Java schreiben, sieht es normalerweise so aus

ThreadClass task = new ThreadClass();
Thread runner = new Thread(task);
runner.start();

Das ThreadClasshier verwendete wäre benutzerdefiniert. Diese Klasse implementiert die RunnableSchnittstelle, die zum Erstellen von Threads erforderlich ist. In dem ThreadClassdas run()Verfahren (nur Verfahren in Runnable) muss auch umgesetzt werden. Es ist klar, dass loswerdenThreadClass effizienter wäre , und genau deshalb gibt es anonyme innere Klassen.

Schauen Sie sich den folgenden Code an

Thread runner = new Thread(new Runnable() {
    public void run() {
        //Thread does it's work here
    }
});
runner.start();

Dieser Code ersetzt den Verweis taskim obersten Beispiel. Anstatt eine separate Klasse zu haben, gibt die anonyme innere Klasse im Thread()Konstruktor ein unbenanntes Objekt zurück, das die RunnableSchnittstelle implementiert und die run()Methode überschreibt . Die Methode run()würde Anweisungen enthalten, die die vom Thread geforderte Arbeit erledigen.

Bei der Beantwortung der Frage, ob anonyme innere Klassen einer der Vorteile von Java sind, muss ich sagen, dass ich nicht ganz sicher bin, da ich derzeit nicht mit vielen Programmiersprachen vertraut bin. Aber was ich sagen kann ist, dass es definitiv eine schnellere und einfachere Methode zum Codieren ist.

Referenzen: Sams bringt sich Java in der siebten Ausgabe von 21 Tagen bei


0

Ein weiterer Vorteil:
Wie Sie wissen, unterstützt Java keine Mehrfachvererbung. Wenn Sie also die Klasse "Thread" als anonyme Klasse verwenden, bleibt der Klasse noch ein Speicherplatz für jede andere Klasse zur Erweiterung übrig.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.