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?
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?
Antworten:
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.
overloading methods
und nicht overriding methods
?
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.
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");
}
});
}
}
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.
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).
myButton.addActionListener(e -> { /* do stuff here */})
oder myButton.addActionListener(stuff)
wäre terser.
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.
Richtlinien für anonyme Klassen.
Die anonyme Klasse wird gleichzeitig deklariert und initialisiert.
Die anonyme Klasse muss auf eine und nur eine Klasse bzw. Schnittstelle erweitert oder implementiert werden.
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
}
});
ref.getClass().newInstance()
.
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).
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
Ich benutze anonyme Objekte zum Aufrufen neuer Threads.
new Thread(new Runnable() {
public void run() {
// you code
}
}).start();
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 Person
Klasse 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 CheckPerson
ist 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 CheckPerson
es 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 Predicate
anstelle der Schnittstelle eine Standardfunktionsschnittstelle verwenden CheckPerson
, wodurch die erforderliche Codemenge weiter reduziert wird.
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.
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 HeavyClass
der zu finalisierenden Methode aufzurufen .
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.
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!) .
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();
}
}
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 new
Schlü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 ThreadClass
hier verwendete wäre benutzerdefiniert. Diese Klasse implementiert die Runnable
Schnittstelle, die zum Erstellen von Threads erforderlich ist. In dem ThreadClass
das 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 task
im obersten Beispiel. Anstatt eine separate Klasse zu haben, gibt die anonyme innere Klasse im Thread()
Konstruktor ein unbenanntes Objekt zurück, das die Runnable
Schnittstelle 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