Java 8 qui est sur le point de sortir nous promet encore plus de programmation fonctionnelle.
Le parcours du tutoriel en ligne justifie cela par l’efficacité de l’approche. Les lambdas expressions permettent en effet de décrire des fonctions anonymes, sans nom et raccrochées à aucun objet. Sans les lambda expression le langage oblige alors à créer une classe (anonyme si nécessaire) et une méthode. Inutile et superflu parait-il…
Supposons que l’on veuille pouvoir passer un comportement à une méthode. Comme on n’avait pas de pointeur sur fonction en Java jusque là, on devait définir une interface puis l’implémenter et passer une instance implémentante à la méthode.
Une interface:
public interface Calculator { int function(int x); }
Une implémentation:
public class InterfaceImplementor implements Calculator { Override public int function(int x) { return 222; } }
définition de la méthode paramétrable par une interface fonctionnelle:
int calcul(Calculator c){ return c.function(0); }
L’appel se fait alors par un code (encore du code) du genre:
calcul(new InterfaceImplementor());
On peut éviter de définir une classe en créant une instance d’une classe anonyme:
calcul(new Calculator(){ @Override public int function(int x) { return 999; }
Le JDK 8 arrive pour éviter encore plus de code en proposant de définir une lambda expression c’est-à-dire une méthode anonyme sans classe. Pour pouvoir tester cette future nouvelle fonctionnalité il faut télécharger le JDK 8 (non finalisé) et Netbean 7.4 (non finalisé) pour pouvoir écrire:
calcul((x) -> 555);
Il y a là une petite complexité (pardon nouveauté) sous le clavier. Le compilateur autorise ici 2 raccourcis: pas d’interface, pas de méthode. La simple définition d’une fonction qui répond au typage de la méthode de l’interface Calculator suffit pour que le compilateur câble tout correctement. On peut se dire que c’est une bonne chose mais là où ça se complique c’est que Java 8 offre la possibilité d’utiliser une méthode existante sans passer par la case refactoring pour faire rentrer la méthode existante dans le moule d’une interface. Ainsi on peut utiliser n’importe quelle méthode ayant la bonne signature fonctionnelle: dans notre exemple int -> int. L’appel ressemble alors à cela:
calcul(ecxistingObject::existingMethod);
Voici le code complet permettant de voir l’ensemble des évolutions du langage abordées dans ce billet:
Une interface pour la manière de faire « old school »:
package lambda; public interface Calculator { int function(int x); }
et son implémentation:
package lambda; public class InterfaceImplementor implements Calculator{ @Override public int function(int x) { return 222; } }
Une classe existante dont on peut réutiliser une méthode sans avoir à implémenter l’interface vue plus haut:
package lambda; public class ExistingClass { private int i = 777; public int uneFonction(int x) { return i; } }
Et les différentes manières de passer une fonction à une méthode:
package lambda; public class Lambda { /** * @param args the command line arguments */ public static void main(String[] args) { // TODO code application logic here Lambda l = new Lambda(); int i = 0; // old school, plain class creation i = l.calcul(new InterfaceImplementor()); System.out.println(i); // old school, anomymous class i = l.calcul(new Calculator(){ @Override public int function(int x) { return 999; } }); System.out.println(i); // anonymous method, lambda expression i = l.calcul((x) -> 555); System.out.println(i); // method reference ! ExistingClass existingObject = new ExistingClass(); i = l.calcul(existingObject::uneFonction); System.out.println(i); } int calcul(Calculator c){ return c.function(0); } }
Dont l’exécution donne:
run: 222 999 555 777 BUILD SUCCESSFUL (total time: 1 second)Tags: Java