Maison >Java >javaDidacticiel >Description de la syntaxe de l'expression Lambda en Java

Description de la syntaxe de l'expression Lambda en Java

高洛峰
高洛峰original
2017-01-23 15:42:101807parcourir

Description de la syntaxe

Une expression lambda se compose des parties suivantes :

1. Une liste de paramètres formels séparés par des virgules entre parenthèses. La méthode CheckPerson.test contient un paramètre p, qui représente une instance de la classe Person. Remarque : Le type du paramètre dans l'expression lambda peut être omis ; de plus, s'il n'y a qu'un seul paramètre, même les parenthèses peuvent être omises. Par exemple, le code mentionné dans la section précédente :

p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
    && p.getAge() <= 25

2. Symbole fléché : ->. Utilisé pour séparer les paramètres et les corps de fonction.

3. Corps fonctionnel. Se compose d’une expression ou d’un bloc de code. Dans l'exemple de la section précédente, une expression comme celle-ci a été utilisée :

   
p.getGender() == Person.Sex.MALE
      && p.getAge() >= 18
      && p.getAge() <= 25

Si une expression est utilisée, le runtime Java calculera et renverra la valeur de l'expression. De plus, vous pouvez également choisir d'utiliser l'instruction return dans le bloc de code :

p -> {
  return p.getGender() == Person.Sex.MALE
      && p.getAge() >= 18
      && p.getAge() <= 25;
}

Cependant, l'instruction return n'est pas une expression. Dans les expressions lambda, les instructions doivent être placées entre accolades. Cependant, il n'est pas nécessaire de placer les instructions entre accolades lors de l'appel d'une méthode qui renvoie une valeur nulle, donc l'écriture suivante est également correcte :

email -> System.out.println(email)
.

Les expressions Lambda et les déclarations de méthodes se ressemblent beaucoup. Par conséquent, les expressions lambda peuvent également être considérées comme des méthodes anonymes, c'est-à-dire des méthodes sans noms définis.

Les expressions lambda mentionnées ci-dessus sont toutes des expressions qui n'utilisent qu'un seul paramètre comme paramètre formel. La classe d'instance suivante, Cauulator, montre comment utiliser plusieurs paramètres comme paramètres formels :

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));
  }
}

La méthode operaBinary dans le code utilise deux paramètres entiers pour effectuer des opérations arithmétiques. L'opération arithmétique ici elle-même est une instance de l'interface IntegerMath. Dans le programme ci-dessus, deux opérations arithmétiques sont définies à l'aide d'expressions lambda : l'addition et la soustraction. Le programme d'exécution imprimera le contenu suivant :

40 + 2 = 42
20 - 10 = 10

Accès aux variables locales des classes externes

Semblables aux classes locales ou aux classes anonymes, les expressions lambda peuvent également accéder aux variables locales des classes externes cours . La différence est qu'il n'est pas nécessaire de prendre en compte des problèmes tels que l'écrasement lors de l'utilisation d'expressions lambda. Une expression lambda n'est qu'un concept lexical, ce qui signifie qu'elle n'a pas besoin d'hériter de noms de la superclasse et qu'elle n'introduit pas non plus de nouvelles portées. Autrement dit, une déclaration dans une expression lambda a la même signification qu'une déclaration dans son environnement externe. Ceci est démontré dans l'exemple suivant :

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);
  }
}


Ce code affichera ce qui suit :

   
x = 23
y = 23
this.x = 1
LambdaScopeTest.this.x = 0


Si le paramètre y dans l'expression lambda myConsumer dans l'exemple est remplacé par x, le compilateur signalera une erreur :

Consumer<integer> myConsumer = (x) ->{
      // ....
    };


Le Le message d'erreur du compilateur est : "la variable x est déjà définie dans la méthode methodInFirstLevel(int)", ce qui signifie que la variable x a été définie dans la méthode methodInFirstLevel. L'erreur est signalée car les expressions lambda n'introduisent pas de nouvelles étendues. Par conséquent, vous pouvez accéder directement aux champs, méthodes et paramètres formels de la classe externe dans les expressions lambda. Dans cet exemple, l'expression lambda myConsumer accède directement au paramètre formel x de la méthode methodInFirstLevel. Lorsque vous accédez aux membres de classes externes, vous utilisez également directement le mot-clé this. Dans cet exemple, this.x fait référence à FirstLevel.x.

Cependant, comme les classes locales ou les classes anonymes, les expressions lambda ne peuvent accéder qu'aux variables locales ou aux membres externes déclarés finaux (ou équivalents à final). Par exemple, nous supprimons le commentaire avant "x=99" ​​​​dans la méthode methodInFirstLevel dans l'exemple de code :

//如下的语句会导致编译器在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);
};


Car la valeur du paramètre x est modifiée dans cette instruction, cela fait que le paramètre x de methodInFirstLevel n'est plus considéré comme final. Par conséquent, le compilateur Java signalera une erreur du type « les variables locales référencées à partir d'une expression lambda doivent être finales ou effectivement finales » lorsque l'expression lambda accède à la variable locale x.

Type de cible

Comment déterminer le type d'expression lambda. Jetons un coup d'œil au code de contrôle du personnel en âge de servir dans l'armée :

   
p -> p.getGender() == Person.Sex.MALE
       && p.getAge() >= 18
       && p.getAge() <= 25


Ce code a été utilisé à deux endroits :

public static void printPersons( List8abf60ac54173a2785e603c7a1f95b4e roster, CheckPerson tester) - Option 3
public void printPersonsWithPredicate(List8abf60ac54173a2785e603c7a1f95b4e roster, Predicate8abf60ac54173a2785e603c7a1f95b4e tester) - Option 6

Lors de l'appel des printPersons méthode , cette méthode attend un paramètre de type CheckPerson et l'expression ci-dessus est une expression de type CheckPerson. Lors de l’appel de la méthode printPersonsWithPredicate, un paramètre de type Predicate8abf60ac54173a2785e603c7a1f95b4e est attendu et la même expression est de type Predicate8abf60ac54173a2785e603c7a1f95b4e. Ainsi, le type déterminé par le type attendu par la méthode est appelé type cible (en fait, je pense que l'inférence de type dans Scala est plus appropriée ici). Le compilateur Java détermine le type d'une expression lambda via le contexte du type cible ou la position où se trouve l'expression lambda. Cela signifie que les expressions lambda ne peuvent être utilisées que lorsque le compilateur Java peut déduire le type :

déclaration de variable ;

affectation

déclaration de retour ; Initialisation du tableau ;

Paramètre de méthode ou de constructeur ;

Corps de la méthode d'expression lambda

Expression conditionnelle (?:);

Paramètres du type de cible et de la méthode


对于方法参数,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中文网!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn