28 avril 2014

Java 8 : Les Expressions Lambdas et les interfaces fonctionnelles


L'expression Lambda est une manière simplifiée d'écrire l'implémentation d'une interface qui n'a qu'une seule méthode à implémenter.

Les paramètres et le code du corps de l'expression Lambda dépendent de la signature de la méthode de l'interface.

Syntaxe : l'expression Lambda s'écrit de la manière suivante :

paramètres -> corps de l'expression Lambda
, avec :
  • paramètres représente les paramètres de la méthode.
    • les paramètres sont indiqués entre parenthèses (a, b, c)
    • s'il n'y a pas de paramètre, il suffit de mettre des parenthèses vides ()
    • les types des arguments ne sont pas indiqués : ils sont déjà définis dans l'interface
  • corps de la Lambda Expression est le corps de la méthode.
    • si le corps de la méthode ne fait qu'une ligne, nous n'avons pas besoin d'utiliser d'accolades. L'expression indiquée sera retournée par l'expression Lambda.
    • si le corps fait plusieurs lignes, nous pouvons utiliser des accolades comme dans une méthode classique. Il faut alors utiliser le mot clé return pour indiquer la valeur retournée par l'expression Lambda.
Exemples :
// pas de paramètres et retourne toujours 10
() -> 10

// l'expression suivante :
(x, y) -> x + y 
// est équivalente à : 
(x, y) -> {return x+y;}

Interfaces fonctionnelles (Functional Interfaces)

Une interface fonctionnelle est une interface avec une seule méthode abstraite à implémenter.

Cette interface peut être utilisée pour définir une expression Lambda.

Prenons comme exemple une interface fonctionnelle qui permet d'effectuer une opération sur deux éléments.
public interface Operation {
    public T calcul(T o1, T o2);
}
Nous utilisons cette interface pour calculer la somme de deux nombres à l'aide de l'expression Lambda suivante :
Operation<Integer> somme = (x,y) -> x+y;
Ce qui donne :
somme.calcul(1,2) // résultat : 3


De même, nous pouvons définir les opérations suivantes à l'aide de la même interface Operation :
Operation<Integer> soustraction = (x,y) -> x-y;
Operation<Integer> multiplication = (x,y) -> x*y;
Operation<Integer> division = (x,y) -> x/y;
Ce qui donne :
soustraction.calcul(7,3) // résultat : 4
multiplication.calcul(7,3) // résultat : 21
division.calcul(7,3) // résultat : 2 (arrondi)


Java 8 fournit par défaut l'interface BinaryOperator, similaire à notre interface "Operation", pour réaliser les opérations sur deux éléments.

BinaryOperator

Voici la signature de la méthode de l'interface fonctionnelle BinaryOperator :
R apply(T t, U u)
En réécrivant le code ci-dessus, nous obtenons :
BinaryOperator<Integer> somme = (x,y) -> x+y;
BinaryOperator<Integer> soustraction = (x,y) -> x-y;
BinaryOperator<Integer> multiplication = (x,y) -> x*y;
BinaryOperator<Integer> division = (x,y) -> x/y;
ce qui donne :
somme.apply(1, 2) // résultat : 3
soustraction.apply(7,3) // résultat : 4
multiplication.apply(7,3) // résultat : 21
division.apply(7,3) // résultat : 2 (arrondi)

Interfaces fonctionnelles fournies par défaut en Java 8

Le JDK 8 apporte par défaut d'autres interfaces fonctionnelles que nous verrons dans un prochain article :
  • UnaryOperator
  • BinaryOperator
  • Predicate
  • Consumer
  • Function
  • Supplier