Syntaxbeschreibung
Ein Lambda -Ausdruck besteht aus den folgenden Teilen:
1. Komma-getrennte Liste formaler Parameter in Klammern. Die Methode checkperson.test enthält einen Parameter P, der eine Instanz der Personklasse darstellt. Hinweis: Die Arten von Parametern in Lambda -Ausdrücken können weggelassen werden. Wenn es nur einen Parameter gibt, können auch Klammern weggelassen werden. Zum Beispiel der im vorherige Abschnitt erwähnte Code:
p -> p.getGender () == Person.sex.Male && P.Getage ()> = 18 && P.getage () <= 25
2. Pfeilsymbol: ->. Wird verwendet, um Parameter und Funktionskörper zu trennen.
3. Funktionskörper. Bestehend aus einem Ausdruck oder einem Codeblock. Im Beispiel im vorherigen Abschnitt wurde dieser Ausdruck verwendet:
p.getGender () == Person.sex.Male && P.getage ()> = 18 && P.getage () <= 25
Wenn Sie einen Ausdruck verwenden, berechnet die Java -Laufzeit den Wert des Ausdrucks. Darüber hinaus können Sie auch die Rückgabeanweisung im Codeblock verwenden:
p -> {return p.getGender () == Person.sex.Male && P.Getage ()> = 18 && P.getage () <= 25; }Die Return -Anweisung ist jedoch kein Ausdruck. In Lambda -Ausdrücken muss die Erklärung in lockige Klammern eingeschlossen sein, aber es ist nicht erforderlich, die Aussagen in lockigen Klammern einzuschließen, wenn nur eine Methode mit leerem Rückgabewert aufgerufen wird. Daher ist die folgende Schreibmethode ebenfalls korrekt:
E -Mail -> System.out.println (E -Mail)
Es gibt viele Ähnlichkeiten in der Erklärung von Lambda -Ausdrücken und -methoden. Daher können Lambda -Ausdrücke auch als anonyme Methoden angesehen werden, dh Methoden ohne Namensdefinition.
Die oben genannten Lambda -Ausdrücke sind alle Ausdrücke, die nur einen Parameter als formalen Parameter verwenden. Die folgende Beispielklasse, Caulator, zeigt, wie mehrere Parameter als formale Parameter verwendet werden:
Paket com.zhyea.zytools; public class Calculator {Schnittstelle IntegerMath {int operation (int a, int b); } public int operationBinary (int a, int b, Integermath op) {return op.operation (a, b); } public static void main (String ... args) {Calculator myApp = neuer Calculator (); IntegerMath Addition = (a, b) -> a + b; System.out.println ("40 + 2 =" + MyApp.operateBinary (40, 2, Addition)); System.out.println ("20 - 10 =" + myapp.operateBinary (20, 10, Subtraktion)); }}Die operatingBinary -Methode im Code verwendet zwei Ganzzahlparameter, um arithmetische Operationen durchzuführen. Die arithmetische Operation hier ist selbst ein Beispiel für die Integermath -Schnittstelle. Im obigen Programm werden zwei arithmetische Operationen unter Verwendung von Lambda -Ausdrücken definiert: Addition und Subtraktion. Das Ausführen des Programms druckt den folgenden Inhalt aus:
40 + 2 = 4220 - 10 = 10
Greifen Sie auf lokale Variablen externer Klassen zu
Ähnlich wie bei lokalen Klassen oder anonymen Klassen können Lambda -Ausdrücke auch auf lokale Variablen externer Klassen zugreifen. Der Unterschied besteht darin, dass keine Probleme wie Überschreibungen bei der Verwendung von Lambda -Ausdrücken in Betracht gezogen werden müssen. Der Lambda -Ausdruck ist nur ein lexikalisches Konzept, was bedeutet, dass er keine Namen aus der Superklasse erben und neue Bereiche einführt. Das heißt, die Erklärung in einem Lambda -Ausdruck hat die gleiche Bedeutung wie die Erklärung in ihrer externen Umgebung. Dies wird im folgenden Beispiel gezeigt:
Paket com.zhyea.zytools; import java.util.function.consumer; öffentliche Klasse lambdascopetest {public int x = 0; Klasse FirstLevel {public int x = 1; void methodInfirstlevel (int x) {// Die folgende Anweisung veranlasst den Compiler, einen Fehler zu melden. Consumer <Ganzzahl> myconsumer = (y) -> {System.out.println ("x =" + x); // Anweisung a system.out.println ("y =" + y); System.out.println ("this.x =" + this.x); System.out.println ("lambdascopetest.this.x =" + lambdascopetest.this.x); }; myconsumer.accept (x); }} public static void main (String ... args) {lambdascopetest st = new lambdascopetest (); Lambdascopetest.Firstlevel fl = St.New FirstLevel (); Fl.MethodinFirstlevel (23); }}Dieser Code gibt Folgendes aus:
x = 23y = 23this.x = 1lambdascopetest.this.x = 0
Wenn Sie den Parameter Y im Lambda -Ausdruck Myconsumer im Beispiel mit X ersetzen, meldet der Compiler einen Fehler:
Consumer <Ganzzahl> myconsumer = (x) -> {// ....};Die Compiler -Fehlermeldung lautet: "Die Variable X ist bereits in Method -Methodeninfirstlevel (int) definiert, was bedeutet, dass die Variable X in der Methode methodInfirstlevel definiert wurde. Es wird ein Fehler gemeldet, weil der Lambda -Ausdruck keinen neuen Bereich einführt. Daher können Sie direkt auf die Domänenfelder, Methoden und formalen Parameter der externen Klasse im Lambda -Ausdruck zugreifen. In diesem Beispiel greift der Lambda -Ausdruck Myconsumer direkt auf den Parameter X der Methode MethodinFirstlevel zu. Bei Zugriff auf Mitglieder externer Klassen wird dieses Schlüsselwort auch direkt verwendet. In diesem Beispiel bezieht sich dies.x auf FirstLevel.x.
Wie bei lokalen oder anonymen Klassen können Lambda -Ausdrücke jedoch nur auf lokale Variablen oder externe Mitglieder zugreifen, die als endgültig deklariert sind (oder gleichwertig zu dem endgültigen). Zum Beispiel entfernen wir den Kommentar vor "x = 99" in der Beispiel -Code -Methode, die methodinfirstlevel -Methode:
// Die folgende Anweisung veranlasst den Compiler, einen Fehler zu melden "lokale Variablen, auf die aus einem Lambda -Ausdruck verwiesen wird, muss endgültig oder effektiv endgültig sein" x = 99; Consumer <Ganzzahl> myconsumer = (y) -> {System.out.println ("x =" + x); // Anweisung a system.out.println ("y =" + y); System.out.println ("this.x =" + this.x); System.out.println ("lambdascopetest.this.x =" + lambdascopetest.this.x); };Da der Wert des Parameters x in dieser Anweisung geändert wird, kann der Parameter X von MethodInfirstlevel nicht mehr als endgültig angesehen werden. Daher meldet der Java -Compiler einen Fehler wie "lokale Variablen, auf die aus einem Lambda -Ausdruck verwiesen wird, muss endgültig oder effektiv endgültig sein", wobei der Lambda -Ausdruck auf die lokale Variable x zugreift.
Zieltyp
Wie kann man den Typ des Lambda -Ausdrucks bestimmen? Schauen wir uns den Kodex für die Filterung von Militärpersonal im angemessenen Alter an:
p -> p.getGender () == Person.sex.Male && P.Getage ()> = 18 && P.getage () <= 25
Dieser Code wurde an zwei Stellen verwendet:
öffentliche statische Leereabdruckpersonen (Liste <Person> Dienstplan, Checkperson Tester) - Lösung 3
public void printpersons withpredicate (Liste <Person> Dienstplan, Prädikat <Person> Tester) - Plan VI
Wenn diese Methode die PrintPersons -Methode aufruft, erwartet diese Methode einen Parameter des Checkperson -Typs. Zu diesem Zeitpunkt ist der obige Ausdruck ein Ausdruck des Checkperson -Typs. Beim Aufrufen der PrintPersons -WithPredicate -Methode wird ein Parameter des Typs Prädikat <Person> erwartet. Zu diesem Zeitpunkt ist der gleiche Ausdruck vom Typ Prädikat <person>. Der Typ, der nach dem von der Methode erwarteten Typ bestimmt wird, wird als Zieltyp bezeichnet (tatsächlich denke ich, dass die Typinferenz in Scala hier angemessener ist). Der Java -Compiler bestimmt den Typ eines Lambda -Ausdrucks durch den Kontext des Zieltyps oder die Position, wenn der Lambda -Ausdruck entdeckt wird. Dies bedeutet, dass Lambda -Ausdrücke nur dann verwendet werden können, wenn der Java -Compiler den Typ schließen kann:
Zieltyp und Methodeparameter
Bei Methodenparametern muss sich der Java -Compiler auch auf zwei Sprachmerkmale verlassen, um den Zieltyp zu bestimmen: Überlastungsanalyse und Typparameterinferenz.
Schauen Sie sich die folgenden zwei funktionalen Schnittstellen an (java.lang.runnable und java.util.concurrent.callable <v>):
public interface runnable {void run (); } public interface Callable <V> {v call (); }Die Methode runnable.run () gibt keinen Wert zurück, während die Methode callable.call () sie hat.
Angenommen, wir überlasten die Invoke -Methode wie folgt:
void Invoke (runnable r) {r.run (); } <t> t invoke (Callable <T> c) {return C.Call (); }Welche Methode wird also in der folgenden Erklärung aufgerufen:
String s = invoke(() -> "done");
Invoke (Callable <T>) wird aufgerufen, weil diese Methode einen Rückgabewert aufweist, während Invoke (Runnable <T>) keinen Wert zurückgibt. In diesem Fall ist der Typ des Lambda -Ausdrucks (() -> "fertig") <T>.
Serialisierung
Wenn der Zieltyp eines Lambda -Expression und die von ihm aufgerufenen Parametertypen serialisierbar sind, ist auch die Lambda -Expression serialisierbar. Wie bei inneren Klassen wird die Serialisierung von Lambda -Ausdrücken jedoch stark nicht empfohlen.