Description de la syntaxe
Une expression de lambda se compose des parties suivantes:
1. Liste des 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 de personne. Remarque: Les types de paramètres dans les expressions de lambda peuvent être omis; De plus, s'il n'y a qu'un seul paramètre, les supports peuvent également être omis. 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 de flèche: ->. Utilisé pour séparer les paramètres et les corps de fonction.
3. Corps de fonction. Composé d'une expression ou d'un bloc de code. Dans l'exemple de la section précédente, cette expression a été utilisée:
P.GetGender () == Person.SEX.MALE && P.Getage ()> = 18 && P.Getage () <= 25
Si vous utilisez une expression, le runtime Java calculera et renverra la valeur de l'expression. De plus, vous pouvez également choisir d'utiliser l'instruction RETOUR dans le bloc de code:
p -> {return p.getgender () == Person.sex.male && p.getage ()> = 18 && p.getage () <= 25; }Cependant, l'instruction de retour n'est pas une expression. Dans les expressions de Lambda, l'instruction doit être enfermée en accolades bouclées, mais il n'est pas nécessaire de joindre les instructions en accolades bouclées lors de l'appel d'une méthode avec une valeur de retour vide, de sorte que la méthode d'écriture suivante est également correcte:
Email -> System.out.println (e-mail)
Il existe de nombreuses similitudes dans la déclaration d'expressions et de méthodes de lambda. Par conséquent, les expressions de Lambda peuvent également être considérées comme des méthodes anonymes, c'est-à-dire des méthodes sans définition de nom.
Les expressions lambda mentionnées ci-dessus sont toutes les expressions qui n'utilisent qu'un seul paramètre comme paramètre formel. L'exemple de classe suivante, Caulator, montre comment utiliser plusieurs paramètres comme paramètres formels:
package com.zhyea.zytools; calculatrice de classe publique {interface IntegerMath {int opération (int a, int b); } public int opérationBinary (int a, int b, IntegerMath op) {return op.operation (a, b); } public static void main (String ... args) {calculator myApp = new Calculator (); IntegerMath ajout = (a, b) -> a + b; System.out.println ("40 + 2 =" + myApp.OperateBinary (40, 2, ajout)); System.out.println ("20 - 10 =" + myApp.OperateBinary (20, 10, soustraction)); }}La méthode OperatingBinary dans le code utilise deux paramètres entiers pour effectuer des opérations arithmétiques. L'opération arithmétique ici est elle-même un exemple de l'interface Integermath. Dans le programme ci-dessus, deux opérations arithmétiques sont définies à l'aide d'expressions lambda: addition et soustraction. L'exécution du programme imprimera le contenu suivant:
40 + 2 = 4220 - 10 = 10
Accéder aux variables locales des classes externes
Semblable aux classes locales ou aux classes anonymes, les expressions Lambda peuvent également accéder aux variables locales des classes externes. La différence est qu'il n'est pas nécessaire de considérer des problèmes tels que la remplacement lors de l'utilisation d'expressions Lambda. L'expression de lambda n'est qu'un concept lexical, ce qui signifie qu'il n'a pas besoin d'hériter de noms de la superclasse, ni introduit de nouvelles lunettes. C'est-à-dire que la déclaration dans une expression de lambda a la même signification que la déclaration dans son environnement extérieur. Ceci est démontré dans l'exemple suivant:
package com.zhyea.zytools; import java.util.function.consumer; classe publique LambdascopeTest {public int x = 0; classe FirstLevel {public int x = 1; void MethodInFirstLevel (int x) {// L'instruction suivante fera que le compilateur signale une erreur "les variables locales référencées à partir d'une expression de lambda doivent être finales ou efficacement finales" // x = 99; Consumer <Integer> myConsumer = (y) -> {System.out.println ("x =" + x); // instruction 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 sortira ce qui suit:
x = 23y = 23This.x = 1LambdascopeTest.This.x = 0
Si vous remplacez le paramètre y dans l'expression de lambda MyConsumer dans l'exemple par x, le compilateur rapportera une erreur:
Consumer <Integer> myConsumer = (x) -> {// ....};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. Une erreur est rapportée parce que l'expression de Lambda n'introduit pas une nouvelle portée. Par conséquent, vous pouvez accéder directement aux champs de domaine, aux méthodes et aux paramètres formels de la classe externe dans l'expression de lambda. Dans cet exemple, l'expression de Lambda MyConsumer accède directement au paramètre X de la méthode MethodInFirstLevel. Lorsque vous accédez aux membres des classes externes, ce mot-clé est également utilisé directement. Dans cet exemple, ce.x fait référence à FirstLevel.x.
Cependant, comme les classes locales ou anonymes, les expressions Lambda ne peuvent accéder qu'à des variables locales ou à des membres externes déclarés finaux (ou équivalents à final). Par exemple, nous supprimons le commentaire avant "x = 99" dans l'exemple de méthode de méthode du code.
// L'énoncé suivant amènera le compilateur à signaler une erreur "Les variables locales référencées à partir d'une expression de lambda doivent être finales ou effectivement finales" x = 99; Consumer <Integer> myConsumer = (y) -> {System.out.println ("x =" + x); // instruction a System.out.println ("y =" + y); System.out.println ("this.x =" + this.x); System.out.println ("LambdascopeTest.This.x =" + LambdascopeTest.this.x); };Étant donné que la valeur du paramètre x est modifiée dans cette instruction, le paramètre X de MethodInFirstlevel ne peut plus être considéré comme final. Par conséquent, le compilateur Java rapportera une erreur comme "les variables locales référencées à partir d'une expression de lambda doivent être finales ou efficacement finales" où l'expression de lambda accède à la variable locale x.
Type cible
Comment déterminer le type d'expression de lambda? Jetons un coup d'œil au code pour filtrer le personnel militaire de l'âge approprié:
P -> P.GetGender () == Person.SEX.MALE && P.Getage ()> = 18 && P.Getage () <= 25
Ce code a été utilisé à deux endroits:
Public Static Void Printersons (liste <honomètre>, Tester de CheckPerson) - Solution 3
Public Void PrintPersonsonsWithPredicate (liste <hongel>, prédicat <ponge> Tester) - Plan VI
Lors de l'appel de la méthode PrintPersons, cette méthode attend un paramètre de type CheckPerson. À l'heure actuelle, l'expression ci-dessus est une expression de type CheckPerson. Lors de l'appel de la méthode PrintPersonsonsWithPredicate, un paramètre de type prédicat <ponse> est attendu. À l'heure actuelle, la même expression est de type prédicat <ponse>. Comme cela, 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 de lambda à travers le contexte du type cible ou la position lors de la découverte de l'expression de lambda. Cela signifie que les expressions de lambda ne peuvent être utilisées que lorsque le compilateur Java peut déduire le type:
Type de cible et paramètres de méthode
Pour les paramètres de la méthode, le compilateur Java doit également s'appuyer sur deux fonctionnalités de langue pour déterminer le type de cible: l'analyse de surcharge et l'inférence des paramètres de type.
Jetez un œil aux deux interfaces fonctionnelles suivantes (java.lang.runnable et java.util.concurrent.callable <v>):
Interface publique Runnable {void run (); } interface publique Calable <v> {v call (); }La méthode runnable.run () ne renvoie pas de valeur, tandis que la méthode callable.call () l'a.
Supposons que nous surchargez la méthode invoquée comme celle suivante:
void invoke (runnable r) {r.run (); } <T> T invoke (callable <T> C) {return C.Call (); }Donc, quelle méthode sera appelée dans l'énoncé suivant:
String s = invoke(() -> "done");
Invoke (callable <T>) est appelé car cette méthode a une valeur de retour, tandis qu'Invoke (Runnable <T>) ne renvoie pas de valeur. Dans ce cas, le type d'expression de lambda (() -> "fait") est appelable <T>.
Sérialisation
Si le type cible d'une expression de lambda et les types de paramètres qu'il appelle est sérialisable, l'expression de lambda est également sérialisable. Cependant, comme les classes intérieures, la sérialisation des expressions de lambda n'est fortement pas recommandée.