Les expressions lambda en Java (depuis version 8)

Fabien Brissonneau
Fabien Brissonneau Ajouter un commentaire 8e lecture en min

Les expressions lambdas en Java.

Les expressions lambda sont apparues en Java dans la version 8, c’est-à-dire en mars 2014. Ces expressions lambdas ne sont cependant pas une originalité du langage Java. Elles apparaissent dans la notion de lambda-calcul, qui date du milieu du 20ième siècle. Cette possibilité nouvelle offerte aux développeurs Java risque fort de changer la façon de programmer. En effet, les expressions lambdas ne sont en aucun cas reliées aux langages orientés objet et procurent du confort là où justement le code souffrait de l’extrême conformité du langage aux concepts objets. Pour faire court, une expression lambda est une fonction qui sera créée, c’est-à-dire déclarée et définie, à l’endroit même où elle doit être utilisée. Aucun besoin pour définir une expression lambda en Java de créer une classe, comme on doit le faire lorsqu’une méthode est nécessaire. C’est donc bien la première fois qu’en Java, une fonction peut être amenée sans lui créer une classe. Bien entendu, ce n’est pas pour autant que le code peut être écrit sans classe, car la règle qui dit que le code est écrit dans le contexte d’une classe est toujours d’actualité. Mais dans le cas de l’expression lambda en Java, la fonction peut être déclarée sans lui déclarer une classe à part.

Syntaxe

La syntaxe de l’expression lambda est la suivante : (Type param) -> { corps de la fonction en plusieurs lignes éventuellement } Cette syntaxe rend l’utilisation de l’expression très simple. Là où vous avez besoin d’une fonction (nous verrons plus loin quels sont les cas typiques), vous placerez une expression lambda. Cette syntaxe peut être simplifiée ainsi : (param) -> expression Cette syntaxe simplifiée est valable lorsque le paramètre est seul, et le corps de la fonction est constitué d’une seule ligne, qui est évaluable comme un retour de méthode. A noter qu’il n’y a pas de syntaxe pour signifier le type de retour, donc le type est estimé en fonction du contexte d’appel. Bien entendu, les écritures intermédiaires comme une liste de paramètres sans type ou bien les types de paramètres et une seule ligne sont valables, … toutes les combinaisons sont possibles.

Utilisation

L’utilisation type de l’expression lambda est l’appel d’une interface fonctionnelle, elle aussi une nouveauté de Java en version 8. Les expressions lambdas accèdent aux attributs de la classe englobante sans limitation. Elles accèdent aussi sous condition aux variables locales de la méthode dans laquelle elles sont définies. Cette caractéristique rappelle les possibilités des classes imbriquées en Java. Cette possibilité est mise en œuvre pour la construction de fonctions de rappel sur l’IHM par exemple. On peut donc imaginer le codage de callback en 3 temps. Premier temps, coder une classe qui implémente l’interface attendue. Je ne donne pas d’exemple de code pour ce cas de figure. Second temps, coder une classe interne qui implémente l’interface attendue.
//dans cet exemple, le listener est une classe anonyme
JButton bouton = new JButton("Clique ici") ;
bouton.addActionListener( new ActionListener() {
   @Override
   public void actionPerformed(ActionEvent ae) {
    // faire ce qu’il faut faire}) ;
  }      
});
Troisième temps, si l’interface ne déclare qu’une seule méthode, remplacer la classe anonyme par une expression lambda. Ce type d’interface est appelée interface fonctionnelle depuis Java8.
//dans cet exemple, le listener est fourni sous la forme d’une expression lambda
      JButton bouton = new JButton("Clique ici") ;
       bouton.addActionListener( (evt) -> {
 // faire ce qu’il faut faire
 });
Les expressions lambdas sont utilisables aussi en remplacement les références de méthodes, nouveautés de Java8. Cette syntaxe vous permet de déclarer une variable de type … méthode, en déclarant sa signature, c’est-à-dire le nombre et le type des paramètres. Si l’expression lambda se conforme à ce contrat, elle est utilisable pour affecter la variable.
 //dans cet exemple, la méthode de comparaison
//est la méthode compare de la classe MaClasse
//on utilise une référence de méthode
List<Integer> liste = new ArrayList<>() ;
liste.add(2) ;
liste.add(6) ;
 liste.add(1) ;
liste.sort(MaClasse ::compare) ;
Le code précédent peut être remplacé par la déclaration d’une expression lambda comme suit.
//dans cet exemple, la méthode de comparaison
//est l’expression lambda              
List<Integer> liste = new ArrayList<>() ;
liste.add(2) ;
liste.add(6) ;
liste.add(1) ;
liste.sort((v,w)-> w-v);

Les streams font abondamment usage des expressions lambdas, ou plutôt le développeur conjugue inévitablement les deux. Cette nouveauté permet de construire des suites de fonctions, en pipe-line, et de déclencher ces fonctions au dernier moment.
//dans cet exemple, la méthode forEach
//applique l'expression lambda
List<String> liste  = new ArrayList<>() ;
liste.add("Fabien") ;
liste.add("Xavier") ;
liste.add("Brissonneau") ;
liste.stream().forEach((s) -> System.out.println(s)) ;
 

Limites

Les limites de l’expression lambda sont essentiellement liées à son contexte d’utilisation. Je vais ici en citer deux principales. La première limitation concerne l’absence d’espace des variables propre à l’expression lambda. Cela signifie que les variables paramètres rentrent en collision avec les variables locales de la méthode dans laquelle est définie l’expression. La collision avec les champs attributs de la classe englobante est plus subtile puis qu’en Java, les variables locales masquent les variables champs de données.
//dans cet exemple, evt le paramètre de l'expression lambda
// entre en conflit avec evt la JList
class MaFenetre extends JFrame {
   private List<String> mesDonnees ;
   public MaFenetre() {
      JList evt = new JList() ;
      //…
      JButton bouton = new JButton("Clique ici") ;
      bouton.addActionListener( (evt) -> {
        mesDonnees.add("clic enregistré") ;
        // faire ce qu’il faut faire}) ;
      });
  }
}
La seconde limitation tient surtout à la façon d’écrire l’expression lambda. Il est vite évident que le corps de l’expression va perturber la lecture, somme toute linéaire, que l’on a du corps de la méthode dans laquelle elle est déclarée. Lorsque l’expression fait plusieurs lignes, le développeur perd la visibilité de son code. La conclusion est que l’expression lambda doit être utilisée pour des expressions courtes.
//dans cet exemple, la longueur de l'expression
//commence à être bien grande...
JButton bouton = new JButton("Clique ici") ;
bouton.addActionListener((e) -> {
//faire un code un peu long
            
List<Integer> liste = new ArrayList<>() ;
    //nuit terriblement 
            
   liste.add(2) ;
   liste.add(6) ;
   liste.add(1) ;
   //à la lisibilité
   liste.sort(MaClasse::compare);
});
 

Conclusion

Java dans sa version 8 a introduit un bon nombre de nouveautés dont les expressions lambdas ne sont pas les moindres. Ce premier article est focalisé sur cette nouveauté. Vous avez remarqué que la notion d’expression lambda accompagne très bien d’autres points remarquables que sont les références de méthodes, les streams, les interfaces fonctionnelles.
Partager cet article
Par Fabien Brissonneau Expert en Conception et Développement sur Java et DotNet avec 15 Ans d'Expérience
Fabien, expert en développement, conception et architecture sur les plateformes Java et DotNet, offre ses services depuis plus de 15 ans. Fort de son expérience en C++, il excelle également dans le domaine de la mobilité, ayant créé avec succès des outils pour Android et Windows Phone, certains étant même publiés. En plus de son travail, il consacre une part significative de son temps à la formation, partageant ainsi son expertise avec les acteurs clés de l'industrie. Pour tout ce qui concerne la conception orientée objet sur les plateformes Java et DotNet, Fabien est votre expert de confiance.
Laisser un commentaire