Que signifie lambda avec 2 flèches dans Java 8?
J'ai déjà lu plusieurs tutoriels Java 8.
En ce moment, j'ai rencontré le sujet suivant: java prend-il en charge le Currying?
Ici, je vois le code suivant:
IntFunction<IntUnaryOperator> curriedAdd = a -> b -> a + b;
System.out.println(curriedAdd.apply(1).applyAsInt(12));
Je comprends que cet exemple somme 2 éléments mais je ne peux pas comprendre la construction:
a -> b -> a + b;
Selon la partie gauche de l'expression, cette ligne devrait implémenter la fonction suivante:
R apply(int value);
Avant cela, je n'ai rencontré lambdas qu'avec une seule flèche.
6 réponses
Si vous exprimez cela comme une syntaxe lambda non abrégée ou une syntaxe de classe anonyme Java pré-lambda, ce qui se passe est plus clair...
La question initiale. Pourquoi sont deux flèches? Simple, il y a deux fonctions en cours de définition... La première fonction est une fonction définissant la fonction, la seconde est le résultat de cette fonction, qui se trouve également être une fonction. Chacun nécessite un opérateur ->
pour le définir.
Non-sténographie
IntFunction<IntUnaryOperator> curriedAdd = (a) -> {
return (b) -> {
return a + b;
};
};
Pré-Lambda avant Java 8
IntFunction<IntUnaryOperator> curriedAdd = new IntFunction<IntUnaryOperator>() {
@Override
public IntUnaryOperator apply(final int value) {
IntUnaryOperator op = new IntUnaryOperator() {
@Override
public int applyAsInt(int operand) {
return operand + value;
}
};
return op;
}
};
Un IntFunction<R>
est une fonction de int -> R
. Un IntUnaryOperator
est une fonction int -> int
.
Donc IntFunction<IntUnaryOperator>
est une fonction qui prend un int
comme paramètre et retourne une fonction qui prend un int
comme paramètre et retourne un int
.
a -> b -> a + b;
^ | |
| ---------
| ^
| |
| The IntUnaryOperator (that takes an int, b) and return an int (the sum of a and b)
|
The parameter you give to the IntFunction
Peut-être est-il plus clair si vous utilisez des classes anonymes pour "décomposer" le lambda:
IntFunction<IntUnaryOperator> add = new IntFunction<IntUnaryOperator>() {
@Override
public IntUnaryOperator apply(int a) {
return new IntUnaryOperator() {
@Override
public int applyAsInt(int b) {
return a + b;
}
};
}
};
Ajouter des parenthèses peut rendre cela plus clair:
IntFunction<IntUnaryOperator> curriedAdd = a -> (b -> (a + b));
Ou probablement variable intermédiaire peut aider:
IntFunction<IntUnaryOperator> curriedAdd = a -> {
IntUnaryOperator op = b -> a + b;
return op;
};
Réécrivons cette expression lambda avec des parenthèses pour la rendre plus claire:
IntFunction<IntUnaryOperator> curriedAdd = a -> (b -> (a + b));
Nous déclarons donc une fonction prenant un int
qui renvoie un Function
. Plus précisément, la fonction renvoyée prend un int
et renvoie un int
(la somme des deux éléments): ceci peut être représenté par un IntUnaryOperator
.
Par conséquent, curriedAdd
est une fonction prenant un int
et retournant un IntUnaryOperator
, donc elle peut être représentée comme IntFunction<IntUnaryOperator>
.
C'est deux expressions lambda.
IntFunction<IntUnaryOperator> curriedAdd =
a -> { //this is for the fixed value
return b -> { //this is for the add operation
return a + b;
};
}
IntUnaryOperator addTwo = curriedAdd.apply(2);
System.out.println(addTwo.applyAsInt(12)); //prints 14
Si vous regardez IntFunction
il pourrait être plus clair: IntFunction<R>
est un FunctionalInterface
. Il représente une fonction qui prend un int
et renvoie une valeur de type R
.
Dans ce cas, le type de retour R
est aussi un FunctionalInterface
, soit IntUnaryOperator
. Ainsi, la première fonction (externe) renvoie elle-même une fonction.
Dans ce cas: Lorsqu'il est appliqué à une int
, curriedAdd
est censé renvoyer une fonction qui prend encore un int
(et retourne de nouveau int
, parce que c'est ce IntUnaryOperator
faire).
En programmation fonctionnelle, il est courant d'écrire le type d'une fonction comme param -> return_value
et vous voyez exactement cela ici. Donc, le type de curriedAdd
est int -> int -> int
(ou int -> (int -> int)
si vous l'aimez mieux).
La syntaxe lambda de Java 8 va de pair avec cela. Pour définir une telle fonction, vous écrivez
a -> b -> a + b
Qui est très similaire au calcul lambda réel:
λa λb a + b
λb a + b
est une fonction qui prend un seul paramètre b
et retourne une valeur (de la somme). λa λb a + b
est un fonction qui accepte un seul paramètre a
et renvoie une autre fonction d'un seul paramètre. λa λb a + b
renvoie λb a + b
avec a
défini sur la valeur du paramètre.