Heim >Java >javaLernprogramm >Beschreibung der Lambda-Ausdruckssyntax in Java
Syntaxbeschreibung
Ein Lambda-Ausdruck besteht aus den folgenden Teilen:
1. Eine durch Kommas getrennte Liste formaler Parameter in Klammern. Die CheckPerson.test-Methode enthält einen Parameter p, der eine Instanz der Person-Klasse darstellt. Hinweis: Der Typ des Parameters im Lambda-Ausdruck kann weggelassen werden. Wenn nur ein Parameter vorhanden ist, können außerdem auch die Klammern weggelassen werden. Zum Beispiel der im vorherigen 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. Besteht aus einem Ausdruck oder Codeblock. Im Beispiel im vorherigen Abschnitt wurde ein Ausdruck wie dieser verwendet:
p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25
Wenn ein Ausdruck verwendet wird, berechnet die Java-Laufzeit den Wert des Ausdrucks und gibt ihn zurück. Darüber hinaus können Sie auch die Return-Anweisung 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 müssen Anweisungen in geschweifte Klammern eingeschlossen werden. Es ist jedoch nicht erforderlich, Anweisungen in geschweifte Klammern einzuschließen, wenn nur eine Methode aufgerufen wird, die einen Nullwert zurückgibt. Daher ist auch die folgende Schreibweise korrekt:
email -> System.out.println(email)
Lambda-Ausdrücke und Methodendeklarationen sehen sehr ähnlich aus. Daher können Lambda-Ausdrücke auch als anonyme Methoden betrachtet werden, also als Methoden ohne definierten Namen.
Die oben genannten Lambda-Ausdrücke sind alle Ausdrücke, die nur einen Parameter als formalen Parameter verwenden. Die folgende Instanzklasse, Caulator, demonstriert, wie mehrere Parameter als formale Parameter verwendet werden:
package com.zhyea.zytools; public class Calculator { interface IntegerMath { int operation(int a, int b); } public int operateBinary(int a, int b, IntegerMath op) { return op.operation(a, b); } public static void main(String... args) { Calculator myApp = new Calculator(); IntegerMath addition = (a, b) -> a + b; IntegerMath subtraction = (a, b) -> a - b; System.out.println("40 + 2 = " + myApp.operateBinary(40, 2, addition)); System.out.println("20 - 10 = " + myApp.operateBinary(20, 10, subtraction)); } }
Die Methode „operateBinary“ im Code verwendet zwei ganzzahlige Parameter, um arithmetische Operationen auszuführen. Die Rechenoperation hier selbst ist eine Instanz der IntegerMath-Schnittstelle. Im obigen Programm werden zwei arithmetische Operationen mithilfe von Lambda-Ausdrücken definiert: Addition und Subtraktion. Das Ausführungsprogramm gibt den folgenden Inhalt aus:
40 + 2 = 42 20 - 10 = 10
Zugriff auf lokale Variablen externer Klassen
Ähnlich wie lokale Klassen oder anonyme Klassen können Lambda-Ausdrücke auch auf lokale Variablen externer Klassen zugreifen Klassen . Der Unterschied besteht darin, dass bei der Verwendung von Lambda-Ausdrücken keine Probleme wie das Überschreiben berücksichtigt werden müssen. Ein Lambda-Ausdruck ist lediglich ein lexikalisches Konzept, was bedeutet, dass er weder Namen von der Oberklasse erben muss noch neue Bereiche einführt. Das heißt, eine Deklaration innerhalb eines Lambda-Ausdrucks hat dieselbe Bedeutung wie eine Deklaration in seiner externen Umgebung. Dies wird im folgenden Beispiel demonstriert:
package com.zhyea.zytools; import java.util.function.Consumer; public class LambdaScopeTest { public int x = 0; class FirstLevel { public int x = 1; void methodInFirstLevel(int x) { //如下的语句会导致编译器在statement A处报错“local variables referenced from a lambda expression must be final or effectively final” // x = 99; Consumer<integer> myConsumer = (y) ->{ System.out.println("x = " + x); // Statement 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 = 23 y = 23 this.x = 1 LambdaScopeTest.this.x = 0
Wenn der Parameter y im Lambda-Ausdruck myConsumer im Beispiel durch x ersetzt wird, meldet der Compiler einen Fehler:
Consumer<integer> myConsumer = (x) ->{ // .... };
The Die Compiler-Fehlermeldung lautet: „Variable x ist bereits in der Methode methodInFirstLevel(int) definiert“, was bedeutet, dass die Variable x in der Methode methodInFirstLevel definiert wurde. Der Fehler wird gemeldet, weil Lambda-Ausdrücke keine neuen Bereiche einführen. Daher können Sie in Lambda-Ausdrücken direkt auf die Feldfelder, Methoden und formalen Parameter der externen Klasse zugreifen. In diesem Beispiel greift der Lambda-Ausdruck myConsumer direkt auf den formalen Parameter x der Methode methodInFirstLevel zu. Beim Zugriff auf Mitglieder externer Klassen verwenden Sie auch direkt das Schlüsselwort this. In diesem Beispiel bezieht sich this.x auf FirstLevel.x.
Lambda-Ausdrücke können jedoch wie lokale Klassen oder anonyme Klassen nur auf lokale Variablen oder externe Mitglieder zugreifen, die als final (oder äquivalent zu final) deklariert sind. Beispielsweise entfernen wir den Kommentar vor „x=99“ in der methodInFirstLevel-Methode im Beispielcode:
//如下的语句会导致编译器在statement A处报错“local variables referenced from a lambda expression must be final or effectively final” x = 99; Consumer<integer> myConsumer = (y) ->{ System.out.println("x = " + x); // Statement 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 geändert wird In dieser Anweisung wird dadurch der Parameter x von methodInFirstLevel nicht mehr als endgültig betrachtet. Daher meldet der Java-Compiler einen Fehler wie „Lokale Variablen, auf die von einem Lambda-Ausdruck verwiesen wird, müssen endgültig oder tatsächlich endgültig sein“, wenn der Lambda-Ausdruck auf die lokale Variable x zugreift.
Zieltyp
So bestimmen Sie den Typ des Lambda-Ausdrucks. Werfen wir einen Blick auf den Code für die Überprüfung von Personal im Wehrdienstalter:
p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25
Dieser Code wurde an zwei Stellen verwendet:
public static void printPersons( List8abf60ac54173a2785e603c7a1f95b4e Roster, CheckPerson Tester) – Option 3
public void printPersonsWithPredicate(List8abf60ac54173a2785e603c7a1f95b4e Roster, Predicate8abf60ac54173a2785e603c7a1f95b4e Tester) – Option 6
Beim Aufrufen der printPersons Methode: Diese Methode erwartet einen Parameter vom Typ CheckPerson und der obige Ausdruck ist ein Ausdruck vom Typ CheckPerson. Beim Aufruf der printPersonsWithPredicate-Methode wird ein Parameter vom Typ Predicate8abf60ac54173a2785e603c7a1f95b4e erwartet, und derselbe Ausdruck ist vom Typ Predicate8abf60ac54173a2785e603c7a1f95b4e. Auf diese Weise wird der Typ, der durch den von der Methode erwarteten Typ bestimmt wird, als Zieltyp bezeichnet (tatsächlich denke ich, dass die Typinferenz in Scala hier besser geeignet ist). Der Java-Compiler bestimmt den Typ eines Lambda-Ausdrucks anhand des Kontexts des Zieltyps oder der Position, an der sich der Lambda-Ausdruck befindet. Das bedeutet, dass Lambda-Ausdrücke nur dort verwendet werden können, wo der Java-Compiler den Typ
Variable Deklaration
Return Statement; Array-Initialisierung;
Methoden- oder Konstruktorparameter;
Bedingter Ausdruck (?:);
Zieltyp und Methodenparameter对于方法参数,Java编译器还需要依赖两个语言特性来决定目标类型:重载解析和类型参数推断。
看一下下面的这两个函数式接口( java.lang.Runnable and java.util.concurrent.Callabled94943c0b4933ad8cac500132f64757f):
public interface Runnable { void run(); } public interface Callable<v> { V call(); }
Runnable.run()方法没有返回值,而Callable.call()方法有。
假设我们像下面这样重载了invoke方法:
void invoke(Runnable r) { r.run(); } <t> T invoke(Callable<t> c) { return c.call(); }
那么在下面的语句中将会调用哪个方法呢:
String s = invoke(() -> "done");
调用的是invoke(Callable8742468051c85b06f0a0af9e3e506b5c),因为这个方法有返回值,而invoke(Runnable8742468051c85b06f0a0af9e3e506b5c)没有返回值。在这种情况下lambda表达式(() -> “done”)的类型是Callable8742468051c85b06f0a0af9e3e506b5c。
序列化
如果一个lambda表达式的目标类型还有它调用的参数的类型都是可序列化的,那么lambda表达式也是可序列化的。然而就像内部类一样,强烈不建议对lambda表达式进行序列化。
更多java中lambda表达式语法说明相关文章请关注PHP中文网!