Prérequis, savoir ce qu'est un Prédicat.
Les lambdas
En Java 8, les lambdas se présentent sous la forme de clojures passables en paramètre à d'autres fonctions ou à des méthodes.
Les prédicats de Java 8 utilisent cette syntaxe pour laquelle nous allons voir quelques exemples. Nous partirons systématiquement de l'écriture la plus typée à l'écriture la moins typée. En effet, en programmation fonctionnelle les types sont inférés, ce qui signifie qu'ils ne sont plus écrits par le développeur mais calculés au moment de l'exécution par la JVM.
L'interface Predicate de Java 8
La documentation de l'interface Predicate est disponible ici ; et la méthode qui nous intéresse est :
boolean test(T t);
Celle-ci caractérise un prédicat, en effet nous voyons bien qu'elle prend un paramètre et qu'elle retourne un booléen en fonction du test exécuté. Notre lambda va donc automatiquement se substituer à cette interface si elle était attendue en paramètre.
Prédicat sans paramètre :
Ces deux écritures sont équivalentes.
// Avec return explicite :
() -> { return true; }
// Avec return implicite :
() -> true
Il est clair que dans le second cas le code est nettement plus concis, nous allons voir que les possibilités s'étendent ensuite.
Prédicat à un paramètre :
Ces quatre écritures sont toutes équivalentes.
// 1) Avec le type explicite et le return explicite :
(Person person) -> { return person.isMale(); }
// 2) Avec le type explicite et le return implicite :
(Person person) -> person.isMale()
// 3) Avec le type implicite et le return explicite :
(person) -> { return person.isMale(); }
// 4) Avec le type implicite et le return implicite :
(person) -> person.isMale()
Comme vous pouvez le constater, l'absence de typage explicite raccourcis le code mais cela se fait en perdant de l'information. Il devient nécessaire au développeur :
- Soit d'avoir un code clair autour de cette lambda et de correctement nommer le paramètre person (quid de la lisibilité si nous l'avions appelé "p").
- Soit d'exécuter le code en mode debug pour savoir quel est le type du paramètre et donc quelles sont les méthodes invocables sur lui.
Prédicat à deux paramètres :
Le principe du typage explicite / implicite étant vu plus haut, je vais uniquement utiliser le typage implicite pour cette illustration afin de réduire la combinatoire d'exemples. Ces écritures demeurent toutes équivalentes.
// Avec le typage explicite :
(Person persA, Person persB) -> Object.equals(persA, persB)
// Avec le typage partiellement explicite :
(Person persA, persB) -> Object.equals(persA, persB)
// Avec le typage implicite :
(persA, persB) -> Object.equals(persA, persB)
Remarques :
Vous avec pu le noter, mais les lambdas ne portent pas de nom. Il est très important de rester conscient que lors de la maintenance, cela revient à lire les instructions d'une fonction pour en deviner l'intention plutôt que de lire son nom ; ce qui est l'inverse d'un clean code.
Bref l'inférence de type c'est sympa mais veillez bien à ne pas trop en abuser. Souvenons-nous de la mode JQuery où les programmes se retrouvaient avec des clojures imbriquées à N niveaux de profondeur et des callbacks dans tous les sens. Un usage parcimonieux est donc de rigueur comme avec toute techno.
Par conséquence vos lambda doivent être courtes et utilisées dans un contexte précis sans les imbriquées les unes dans les autres d'une manière générale. Pour le reste, ce n'est que du bonheur !