Da Java8 kürzlich veröffentlicht wurde und seine brandneuen Lambda-Ausdrücke wirklich cool aussehen, habe ich mich gefragt, ob dies den Abbruch der anonymen Klassen bedeutet, an die wir uns so gewöhnt haben.
Ich habe ein wenig darüber recherchiert und einige coole Beispiele dafür gefunden, wie Lambda-Ausdrücke diese Klassen systematisch ersetzen werden, beispielsweise die Sortiermethode der Collection, mit der eine anonyme Instanz von Comparator für die Sortierung verwendet wurde:
Collections.sort(personList, new Comparator<Person>(){
public int compare(Person p1, Person p2){
return p1.firstName.compareTo(p2.firstName);
}
});
Jetzt kann mit Lambdas gearbeitet werden:
Collections.sort(personList, (Person p1, Person p2) -> p1.firstName.compareTo(p2.firstName));
Und sieht überraschend prägnant aus. Meine Frage ist also: Gibt es einen Grund, diese Klassen in Java8 anstelle von Lambdas zu verwenden?
EDIT
Gleiche Frage, aber in umgekehrter Richtung: Welche Vorteile bietet die Verwendung von Lambdas anstelle von Anonymous-Klassen? Da Lambdas nur mit Schnittstellen für einzelne Methoden verwendet werden kann, ist diese neue Funktion nur eine Abkürzung, die nur in wenigen Fällen verwendet wird, oder ist sie wirklich nützlich?
Eine anonyme innere Klasse (AIC) kann verwendet werden, um eine Unterklasse einer abstrakten Klasse oder einer konkreten Klasse zu erstellen. Ein AIC kann auch eine konkrete Implementierung einer Schnittstelle bereitstellen, einschließlich des Hinzufügens von Status (Feldern). Auf eine Instanz einer AIC kann in ihren Methodenkörpern mit this
verwiesen werden, sodass weitere Methoden aufgerufen werden können, ihr Status im Laufe der Zeit verändert werden kann usw. Keine dieser Methoden trifft auf Lambdas zu.
Ich vermute, dass die meisten AIC-Anwendungen zustandslose Implementierungen einzelner Funktionen zur Verfügung stellen und daher durch Lambda-Ausdrücke ersetzt werden können. Es gibt jedoch auch andere AIC-Anwendungen, für die Lambdas nicht verwendet werden können. AICs sind hier zu bleiben.
UPDATE
Ein weiterer Unterschied zwischen AICs und Lambda-Ausdrücken besteht darin, dass AICs einen neuen Anwendungsbereich einführen. Das heißt, Namen werden von den Oberklassen und Schnittstellen der AIC aufgelöst und können Namen spiegeln, die in der lexzial einschließenden Umgebung vorkommen. Bei Lambdas werden alle Namen lexikalisch aufgelöst.
Lambdas sind zwar eine großartige Funktion, funktionieren jedoch nur mit SAM-Typen. Das heißt, Schnittstellen nur mit einer einzigen abstrakten Methode. Es würde fehlschlagen, sobald Ihre Schnittstelle mehr als eine abstrakte Methode enthält. Hier werden anonyme Klassen nützlich sein.
Nein, wir können anonyme Klassen nicht einfach ignorieren. Und nur zu Ihrer Information, Ihre sort()
-Methode kann vereinfacht werden, indem Sie die Typdeklaration für p1
und p2
überspringen:
Collections.sort(personList, (p1, p2) -> p1.firstName.compareTo(p2.firstName));
Sie können hier auch die Methodenreferenz verwenden. Sie fügen entweder eine compareByFirstName()
-Methode in der Person
-Klasse hinzu und verwenden:
Collections.sort(personList, Person::compareByFirstName);
oder fügen Sie einen Getter für firstName
hinzu, rufen Sie direkt die Comparator
aus der Comparator.comparing()
-Methode ab:
Collections.sort(personList, Comparator.comparing(Person::getFirstName));
Lambda-Performance mit anonymen Klassen
Beim Start der Anwendung muss jede Klassendatei geladen und überprüft werden.
Anonyme Klassen werden vom Compiler als neuer Untertyp für die angegebene Klasse oder Schnittstelle verarbeitet, sodass für jede Klasse eine neue Klassendatei generiert wird.
Lambdas unterscheiden sich bei der Bytecode-Generierung, sie sind effizienter und werden mit invokedynamic-Anweisungen verwendet, die mit JDK7 geliefert werden.
Bei Lambdas wird diese Anweisung verwendet, um die Übersetzung des Lambda-Ausdrucks im Bytecode bis zur Laufzeit zu verzögern. (Anweisung wird nur zum ersten Mal aufgerufen)
Als Ergebnis wird der Lambda-Ausdruck zu einer statischen Methode (zur Laufzeit erstellt). (Es gibt einen kleinen Unterschied zwischen Stateles und Statefull-Fällen, sie werden über generierte Methodenargumente aufgelöst)
Es gibt folgende Unterschiede:
1) Syntax
Lambda-Ausdrücke sehen im Vergleich zu Anonymous Inner Class (AIC) sauber aus
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("in run");
}
};
Thread t = new Thread(r);
t.start();
}
//syntax of lambda expression
public static void main(String[] args) {
Runnable r = ()->{System.out.println("in run");};
Thread t = new Thread(r);
t.start();
}
2) Geltungsbereich
Eine anonyme innere Klasse ist eine Klasse. Dies bedeutet, dass innerhalb der inneren Klasse ein Variablenbereich definiert ist.
Lambda expression ist zwar kein eigener Bereich, sondern Teil des einschließenden Geltungsbereichs.
Eine ähnliche Regel gilt für super und this, wenn innerhalb anonymer innerer Klassen und Lambda-Ausdrücke verwendet wird. Im Falle einer anonymen inneren Klasse bezieht sich dieses Schlüsselwort auf den lokalen Bereich und ein Super-Schlüsselwort auf die Superklasse der anonymen Klasse. Im Falle eines Lambda-Ausdrucks bezieht sich dieses Schlüsselwort auf das Objekt des umgebenden Typs und super auf die Oberklasse der umgebenden Klasse.
//AIC
public static void main(String[] args) {
final int cnt = 0;
Runnable r = new Runnable() {
@Override
public void run() {
int cnt = 5;
System.out.println("in run" + cnt);
}
};
Thread t = new Thread(r);
t.start();
}
//Lambda
public static void main(String[] args) {
final int cnt = 0;
Runnable r = ()->{
int cnt = 5; //compilation error
System.out.println("in run"+cnt);};
Thread t = new Thread(r);
t.start();
}
3) Leistung
Zur Laufzeit erfordern anonyme innere Klassen das Laden von Klassen, die Speicherzuordnung und die Objektinitialisierung sowie den Aufruf einer nicht statischen Methode, während der Lambda-Ausdruck reine Kompilierungszeit ist und während der Laufzeit keine zusätzlichen Kosten entstehen. Daher ist die Leistung des Lambda-Ausdrucks besser als bei anonymen inneren Klassen. **
** Mir ist klar, dass dieser Punkt nicht ganz richtig ist. Bitte beziehen Sie sich auf die folgende Frage für Details. Lambda vs. anonyme Leistung in der inneren Klasse: Verringerung der Belastung des ClassLoader?
Lambda's in Java 8 wurde zur funktionalen Programmierung eingeführt. Wo Sie Boilerplate-Code vermeiden können. Ich bin auf diesen interessanten Artikel über Lambda gestoßen.
http://radar.oreilly.com/2014/04/whats-new-in-Java-8-lambdas.html
Es ist ratsam, für einfache Logik Lambda-Funktionen zu verwenden. Wenn das Implementieren einer komplexen Logik mit Lambdas im Fehlerfall einen erheblichen Aufwand beim Debuggen des Codes zur Folge hat.
invoke dynamic
werden Lambda nicht zurück in die anonymen Klassen konvertiert während der Kompilierzeit (Java muss keine Objekte erstellen, muss sich nur um die Signatur der Methode kümmern, kann sich an die Methode binden, ohne ein Objekt zu erstellenVergleichen wir den Unterschied zwischen der Lambda-Expression und der Anonymous-Klasse.
1. Syntax
Anonyme Klasse:
package com.onlyfullstack;
public class LambdaVsAnonymousClass {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Anonymous class");
}
};
Thread thread = new Thread(runnable);
thread.start();
}
}
Lambda:
package com.onlyfullstack;
public class LambdaVsAnonymousClass {
public static void main(String[] args) {
Runnable runnable = () -> System.out.println("Lambda Expression");
Thread thread = new Thread(runnable);
thread.start();
}
}
2. Implementierung
Anonyme Klasse kann verwendet werden, um eine beliebige Schnittstelle mit einer beliebigen Anzahl abstrakter Methoden zu implementierenLambda-Ausdruck funktioniert nur mit SAM-Typen (Single Abstract Method). Das ist eine Schnittstelle mit nur einer einzigen abstrakten Methode, die auch als funktionales Interface bezeichnet wird. Es würde fehlschlagen, sobald Ihre Schnittstelle mehr als eine abstrakte Methode enthält.
3. Zusammenstellung
Anonyme Klasse: Java erstellt zwei Klassendateien für die Java-Datei LambdaVsAnonymousClass als LambdaVsAnonymousClass.class - enthält das Hauptprogramm LambdaVsAnonymousClass $ 1.class - enthält eine anonyme Klasse
Lambda-Ausdruck: Mit Lambda-Ausdruck erstellt der Compiler nur eine Klassendatei wie unten .
Java erstellt also die neue Klassendatei für jede verwendete anonyme Klasse.
4. Leistung
Anonyme Klassen werden vom Compiler als neuer Subtyp für die angegebene Klasse oder Schnittstelle verarbeitet, sodass für jede verwendete anonyme Klasse eine neue Klassendatei generiert wird. Beim Start der Anwendung wird jede Klasse, die für die Anonymous-Klasse erstellt wurde, geladen und überprüft. Dieser Vorgang ist ziemlich zeitaufwändig, wenn Sie über eine große Anzahl anonymer Klassen verfügen.
Lambda-Ausdrücke Anstatt direkte Bytecodes für Lambda zu generieren (wie der vorgeschlagene anonyme Klassen-Syntaxzucker), deklariert der Compiler ein Rezept (über invokeDynamic-Anweisungen) und delegiert den realen Konstruktionsansatz an die Laufzeit.
Lambda-Ausdrücke sind also schneller als die anonymen Klassen, da sie nur ausgeführt werden, wenn sie aufgerufen werden.
Weitere Informationen finden Sie unter den folgenden Links:
https://onlyfullstack.blogspot.com/2019/02/lambda-vs-anonymous-class-in-Java-8.html
https://onlyfullstack.blogspot.com/2019/02/wie-lambda-internally-works-in-Java-8.html
Anonyme Klassen sind da, um zu bleiben, weil Lambda für Funktionen mit einzelnen abstrakten Methoden gut ist, aber in allen anderen Fällen sind anonyme innere Klassen Ihr Retter.