Heim > Java > javaLernprogramm > Hauptteil

Beschreibung der Lambda-Ausdruckssyntax in Java

高洛峰
Freigeben: 2017-01-23 15:42:10
Original
1767 Leute haben es durchsucht

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
Nach dem Login kopieren

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
Nach dem Login kopieren

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;
}
Nach dem Login kopieren

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)
Nach dem Login kopieren

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));
  }
}
Nach dem Login kopieren

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
Nach dem Login kopieren

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);
  }
}
Nach dem Login kopieren


Dieser Code gibt Folgendes aus:

   
x = 23
y = 23
this.x = 1
LambdaScopeTest.this.x = 0
Nach dem Login kopieren


Wenn der Parameter y im Lambda-Ausdruck myConsumer im Beispiel durch x ersetzt wird, meldet der Compiler einen Fehler:

Consumer<integer> myConsumer = (x) ->{
      // ....
    };
Nach dem Login kopieren


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);
};
Nach dem Login kopieren


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
Nach dem Login kopieren


Dieser Code wurde an zwei Stellen verwendet:

public static void printPersons( List Roster, CheckPerson Tester) – Option 3
public void printPersonsWithPredicate(List Roster, Predicate 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 Predicate erwartet, und derselbe Ausdruck ist vom Typ Predicate. 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.Callable):

public interface Runnable {
    void run();
  }
 
  public interface Callable<v> {
    V call();
  }
Nach dem Login kopieren


Runnable.run()方法没有返回值,而Callable.call()方法有。

假设我们像下面这样重载了invoke方法:

void invoke(Runnable r) {
   r.run();
 }
 
 <t> T invoke(Callable<t> c) {
   return c.call();
 }
Nach dem Login kopieren


那么在下面的语句中将会调用哪个方法呢:

String s = invoke(() -> "done");

调用的是invoke(Callable),因为这个方法有返回值,而invoke(Runnable)没有返回值。在这种情况下lambda表达式(() -> “done”)的类型是Callable

序列化

如果一个lambda表达式的目标类型还有它调用的参数的类型都是可序列化的,那么lambda表达式也是可序列化的。然而就像内部类一样,强烈不建议对lambda表达式进行序列化。

更多java中lambda表达式语法说明相关文章请关注PHP中文网!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage