Quelle est la répartition de la syntaxe lambda de Java?

Veuillez expliquer la syntaxe des méthodes lambda de Java 8.

Il y a beaucoup d’explications sur ce que sont les fonctions lambda, mais je ne trouve pas d’explication complète de la syntaxe, et je trouve très difficile d’apprendre à reproduire la syntaxe correctement parce que je ne comprends pas pourquoi re écrit comme ils sont.

Voici un cas courant que je rencontre, avec la permission de NetBeans:

public static void main(Ssortingng[] args) { SwingUtilities.invokeLater(() -> { new MainAppJFrame(); }); } 

Donc, d’une manière ou d’une autre, l’expression lambda suivante se résout en méthode run () d’un object Runnable anonyme:

 () -> { // do stuff } 

Le -> est-ce que la syntaxe lambda est correcte, non? Et les accolades contiennent simplement le code de méthode anonyme. Les parenthèses sont-elles un argument vide, car dans ce cas, nous créons une méthode Runnable.run() ?

Tout cela n’est pas clair pour moi. Je suppose que le compilateur sait instancier une Runnable anonyme en fonction du type attendu par la SwingUtilities.invokeLater(Runnable) ? Que se passerait-il s’il y avait deux méthodes SwingUtilities.invokeLater qui diffèrent uniquement par la liste de parameters? Evidemment il n’y a pas dans ce cas précis, mais c’est possible ailleurs:

 interface ExampleLambdaConsumer { public void doSomething(Runnable r); public void doSomething(java.lang.reflect.Method m); } class C implements ExampleLambdaConsumer { // implementations for doSomething methods here public static void main(Ssortingng[] args) { doSomething(() -> { // lambda method body here } } } 

La syntaxe est la suivante:

 arguments -> body 

où les arguments peuvent être soit

  • ()

  • une seule variable si le type de cette variable peut être déduit du contexte

  • une séquence de variables, avec ou sans types, entre parenthèses

et body peut être soit une expression, soit un bloc {...} avec des instructions. L’expression est simplement retournée, ie () -> 2 est équivalent à () -> {return 2;}


EDIT: En cas d’expressions lambda comme () -> f() :

  • si f() void , ils sont équivalents à () -> { f(); } () -> { f(); }

  • sinon, ils sont équivalents à () -> { f(); } () -> { f(); } ou () -> { return f(); }) () -> { return f(); }) . Le compilateur le déduit du contexte d’appel, mais il préfère généralement ce dernier.

Par conséquent, si vous avez deux méthodes: void handle(Supplier) et void handle(Runnable) , alors:

  • handle(() -> { return f(); }) et handle(() -> x) appellent le premier,

  • handle(() -> { f(); } appellera le second et

  • handle(() -> f()) :

    • si f() retourne void ou un type qui n’est pas convertible en T , alors il appellera le second

    • si f() retourne un type qui est convertible en T , alors il appellera le premier


Le compilateur essaie de faire correspondre le type de lambda au contexte. Je ne connais pas les règles exactes, mais la réponse à:

Que se passerait-il s’il y avait deux méthodes SwingUtilities.invokeLater qui diffèrent uniquement par la liste de parameters?

est: cela dépend de ce que seraient ces listes de parameters. Si l’autre invokeLater avait aussi exactement un paramètre et que ce paramètre serait de type qui est également une interface avec une méthode de type void*() , eh bien, alors il se plaindrait de ne pas savoir quelle méthode vous voulez dire.

Pourquoi sont-ils écrits comme ils sont? Eh bien, je pense que c’est parce que la syntaxe en C # et Scala est presque la même (ils utilisent => plutôt que -> ).

La syntaxe est

 (parameter_list_here) -> { stuff_to_do; } 

Les accolades peuvent être omises s’il s’agit d’une seule expression. Les parenthèses habituelles autour de la liste de parameters peuvent être omises s’il s’agit d’un seul paramètre.

La syntaxe ne fonctionne que pour toutes les interfaces fonctionnelles. L’annotation @FunctionalInterface indique au compilateur que vous avez l’intention d’écrire une telle interface et génère une erreur de compilation si vous ne remplissez pas les conditions requirejses – par exemple, elle ne doit avoir qu’une seule méthode pouvant être remplacée.

 @FunctionalInterface interface TestInterface { void dostuff(); } 

Runnable est aussi déclaré comme ça. Les autres interfaces ne le sont pas et ne peuvent pas être utilisées avec les fonctions lambda.

Maintenant que nous avons créé une nouvelle interface fonctionnelle avec une méthode qui ne prend aucun paramètre, pourquoi ne pas tester la question de la “collision” dans les signatures?

 public class Main { private void test(Runnable r) { } private void test(TestInterface ti) { } public static void main(Ssortingng[] args) { test(() -> { System.out.println("test");}) } @FunctionalInterface interface TestInterface { void dostuff(); } } 

Résultat: erreur de compilation: appel ambigu à la méthode test.

Vous voyez, le compilateur / VM (si cela est fait à l’exécution) trouve les méthodes appropriées et leur liste de parameters et voit si le paramètre est une interface fonctionnelle et s’il crée une implémentation anonyme de cette interface. Techniquement (en code octet), c’est différent d’une classe anonyme, mais sinon identique (vous ne verrez pas les fichiers principaux de $ 1.class).

Votre exemple de code (gracieuseté de Netbeans) peut également être remplacé par

 SwingUtilities.invokeLater(MainAppJFrame::new); 

Btw. 🙂

Les expressions Lambda sont fondamentalement adoptées dans Java 8 pour simplifier la fonction de traitement des fonctions anonymes .

Ils ne sont que des raccourcis pour remplacer les anciennes fonctions anonymes java.

Voir l’exemple suivant:

Supposons que vous ayez une interface A qui n’a qu’une seule méthode déclarée comme ci-dessous:

 interface A{ void print(); } 

maintenant avec l’ ancien style java , nous remplacerons ceci de manière anonyme comme ci-dessous:

 new A() { @Override public void print() { System.out.println("in a print method"); } }; 

en outre maintenant, avec l’expression java 8 lambda, nous allons l’utiliser comme ci-dessous:

 () -> System.out.println("in a print method"); 

Ici, nous pouvons passer les parameters requirejs à la méthode before -> operator puis au corps après -> operator.

le seul paramètre supplémentaire dont nous avons besoin pour y parvenir est que nous devons déclarer une interface avec @FunctionalInterface comme ci-dessous:

  @FunctionalInterface interface A{ void print(); } 

Remarque: – Une expression lambda ne peut être utilisée que pour une interface “fonctionnelle” comportant une seule méthode autre que celle par défaut.