Java 8 : l’ère des expressions Lambda

LambdaJAVA 8 (version SE 8 en Sept 2013, EE 8 en 2015)  arrive avec son lots de nouvelles fonctionnalités et notamment les expressions Lambda susceptibles de révolutionner notre façon de coder mais aussi d'anticiper nos développements. Un bémol néanmoins: JAVA à la sauce Lambda devient un langage ouvert à l'orientation vers les fonctions et moins regardant quant au caractère "fortement typé" qui défini ce langage. Je vous propose de décrypter une partie des fonctionnalités des expressions Lambda afin de vous faire une première idée.

Les fonctions anonymes se distinguent en trois types : les fonctions constantes, les fonctions littérales et les fonctions Lambda. Une fonction anonyme est littéralement une fonction qui n’est pas définie par une déclaration (nom, paramètres, valeur de retour, types …). L’origine de cette appellation provient des recherches d’Alonzo Church sur le Lambda-calcul dans les années 30. Ce système formel (qui peut être ou non typé) a été utilisé pour décrire les fonctions récursives.

1) Fonction Lambda

Les fonctions Lambda décrivent en informatique, par extension ou abus de langage l’ensemble des fonctions anonymes.

Les avantages sont multiples :
- l’utilisation de la fonction à la volée
- la réduction des lignes de code
- la lecture simplifiée, nul besoin de se référer à la déclaration de la fonction
- la facilitation de l’imbrication (l’appel d’une fonction dans une autre)
- Simplifier la mono-utilisation d’une fonction

2) Lambda dans Java 8

De nombreux langages intègrent les fonctions Lambda : JavaScript, C#, Ruby, PHP, C++.
JAVA jusqu’à maintenant se contentait de l’usage de classes abstraites. L’arrivée des expressions Lamda est une des grandes nouveautés de la version 8.
La communauté des développeurs reste encore sceptique quant à son utilité. En effet, cela pourrait remettre en cause la définition de JAVA comme langage orienté objet (ce ne sont plus des objets qui sont passé en paramètres mais des fonctions) ainsi que le typage strict.
Les exemples suivants permettront peut-être de vous faire une opinion :

a) Classes imbriquées

Lors de l’utilisation de librairies pour les interfaces (AWT, Swing…), il est plus simple et plus lisible de décrire les actions d’un bouton comme ceci.

JButton testButton = new JButton("Test Button");
testButton.addActionListener(new ActionListener()
{

    public void actionPerformed(ActionEvent ae)
    {
      System.out.println("Click Detected by Anon Class");

    }

});

C’est-à-dire définir directement objet ActionListener en redéfinissant sa méthode actionPerformed à la volée plutôt que de créer une classe à part qui implémente l’interface ActionListener. Le code reste tout de même un peu « lourd ».

De plus l’interface ActionListener ne contient qu’une méthode abstraite, ce qui en fait une interface « fonctionnelle », leur implémentation dans le code peut alors être facilement remplacée par une expression Lamda.

Une expression Lambda se compose de paramètres, d’un symbole flèche et d’un corps (exécution).

 Liste des Arguments   Symbole Flèche   Corps 
          (int x, int y)              ->   x + y

Dans notre exemple nous pourrions écrire :

JButton testButton = new JButton("Test Button");

testButton.addActionListener(e -> 
{
   System.out.println("Click Detected by Anon Class");
});

Le type de “e” dépend du contexte. Ce qui nous permet aussi de supprimer éventuellement une ligne d’import devenue inutile.

b) Le passage de valeurs désormais ouvert aussi aux comportements

Il vous est sûrement arriver d’avoir besoin de développer des méthodes que vous avez dû copier et puis finalement modifier pour intégrer des cas supplémentaires :
Le principe du « 1. Write 2. Copy 3. Refactor »

Prenons l’exemple suivant :
Une simple liste d’entier de 1 à 6.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

Une methode pour en faire la somme:

public int sumAll(List<Integer> numbers)
{

  int total = 0;
  for (int number : numbers)
  {

     total += number;

  }

  return total;

}

Finalement nous souhaitons aussi une méthode pour faire la somme des nombres paires :

public int sumAllEven(List<Integer> numbers)
{

  int total = 0;

  for (int number : numbers)
  {

    if (number % 2 == 0)
    {

      total += number;

    }
  }

  return total;
}

Que faire dans le cas où il vous est demandé de rajouter un calcul de somme supplémentaire : dans un cas classique, l’idée sera d’adapter la méthode pour qu’elle puisse effectuer toutes les sommes.

Dans le cas des expressions lambda il est aussi possible d’utiliser les « Prédicats » (>Predicate<T>) :

public int sumAll(List<Integer> numbers, Predicate<Integer> p)
{

  int total = 0;

  for (int number : numbers)
  {

    if (p.test(number))
    {
      total += number;

    }

  }

  return total;

}

« Predicate » est une nouvelle interface fonctionnelle ajouté à JAVA 8.
Voilà comment utilisé une méthode rendue plus générique grâce au prédicat et expressions Lambda.

sumAll(numbers, n -> true);
sumAll(numbers, n -> n % 2 == 0);
sumAll(numbers, n -> n > 3);

Les Lambdas apportent aussi d'autres fonctionnalités importantes: comme les méthodes par défaut pour rendre une interface fonctionnelle, l'appelle des méthodes non statiques avec "::" ainsi que la parallélisme et le "laziness".

A vous de jouer !

Sources:

Lire plus:

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.