Prise en main des lambdas expressions dans Java 8

Une des grandes nouveautés de Java 8 est l’arrivée des Lambdas expressions, et donc de la programmation fonctionnelle dans Java. Cela représente un changement majeur de la façon de concevoir l’architecture de son code Java.
Le but de cet article n’est pas de décrire comment fonctionnent les lambdas en Java 8, mais de voir quelques exemples « de base » pour prendre en main rapidement cette nouveauté et voir en quoi cela change de façon majeur l’existant des versions précédentes de Java.

Pour en savoir plus sur le fonctionnement des lambdas et leur syntaxe, vous pouvez vous reporter à la documentation d’oracle (http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html).

Premier exemple : avant java 8

Pour commencer, nous allons reprendre simplement l’exemple proposé par la documentation Oracle.

Pour utiliser Java 8, il vous faut bien sûr le jdk 8 (http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html), ainsi qu’un IDE (Eclipse Luna propose une intégration de Java 8 : https://www.eclipse.org/downloads/).

A partir d’Eclipse, on crée un nouveau projet, avec deux classes pour le moment :
– La classe Main qui contient une méthode public static void main(String[] args)
– La classe Person, qui est une simple entité avec deux attributs, les getters/setters associés et une méthode pour afficher les informations de la personne :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Person {

    private String name;

    private int age;

    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }

    public void printPerson(){
        System.out.println(name + " a " + age + " ans");
    }

    // Getters et Setters
}

On retourne désormais dans la classe Main. Notre objectif est de créer une méthode qui va afficher les personnes plus âgées qu’un certain âge. Cette méthode prendra donc en paramètre une liste de personnes et un âge limite, et ne retournera rien. Elle se contentera d’afficher les personnes correspondant au critère. Sa signature est donc :

void printPersonOlderThan(List, int)

Voici tout d’abord la méthode main() de la classe Main :

public static void main(String[] args) {
     Person p1 = new Person("Thomas", 19);
     Person p2 = new Person("Paul", 25);

     List<Person> persons = new ArrayList<Person>();

     persons.add(p1);
     persons.add(p2);
     printPersonOlderThan(persons, 20);
}

Il reste à écrire la méthode qui affiche les personnes :

public static void printPersonOlderThan(List persons, int age) {
    for (Person p : persons) {
        if (p.getAge() >= age) {
            p.printPerson();
        }
    }
}

Et ça marche !

Paul a 25 ans

Mais ça n’a rien de bien inédit …

Second exemple : avec java 8

On va donc passer à une version utilisant les lambdas, et qui va rendre cette méthode beaucoup plus générique. En effet, au lieu que la méthode affiche les personnes plus âgées qu’un certain âge, elle va afficher les personnes … qui répondent à un certain critère. Ce critère à l’avantage de pouvoir changer à chaque appel de la méthode (par exemple pour afficher les personnes plus jeunes qu’un certain âge …).

Voici le nouveau code de notre méthode :

public static void printPersonsWithPredicate(List<Person> persons, Predicate<Person> tester) {
    for (Person p : persons) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

Un petit mot sur cet objet : Predicate<T>. Il s’agit d’une interface qui ne possède qu’une seule méthode abstraite : boolean test(T t); Une telle interface se nomme « Interface fonctionnelle », et est donc candidate pour utiliser des lambdas expressions.
Cette méthode test(T) effectue un test sur l’objet T, puis retourne vrai ou faux. C’est exactement ce que l’on souhaite : il suffit que la méthode test() se charge de vérifier que la personne est plus âgée qu’un certain âge, ou à l’inverse qu’elle est moins âgée. Pour cela, il suffit de changer l’implémentation de la méthode test() en fonction du traitement que l’on souhaite effectuer, et de passer l’objet Predicate associé en paramètre de la méthode printPersonsWithPredicate().

Et c’est là qu’interviennent les lambdas : plutôt que de créer deux classes qui implémentent l’interface Predicate<T>, ce qui est très lourd pour simplement redéfinir une méthode, on va créer une lambda expression. Voilà le nouveau code de la méthode main() :

public static void main(String[] args) {
    Person p1 = new Person("Thomas", 19);
    Person p2 = new Person("Paul", 25);

    List<Person> persons = new ArrayList<>();

    persons.add(p1);
    persons.add(p2);

    Predicate<Person> plusVieuxQue = (p) -> {
        return p.getAge() >= 20;
    };
    Predicate<Person> plusJeuneQue = (p) -> {
        return p.getAge() < 20;
   };

   printPersonsWithPredicate(persons, plusVieuxQue);
   printPersonsWithPredicate(persons, plusJeuneQue);
}

Le premier appel affichera : Paul a 25 ans
Le second appel affichera : Thomas a 19 ans

On a défini ici une sorte de « méthode anonyme », qui est beaucoup plus légère et plus concise que la création des classes effectuant ce même traitement.

Troisième exemple : pour aller plus loin avec java 8

Et maintenant … Rien n’empêche de refaire la même chose avec le traitement d’affichage des personnes. Ainsi, au lieu que notre méthode affiche les utilisateurs répondant au critère, elle se contentera d’effectuer une action générique.

public static void processPersons(List<Person> persons, Predicate<Person> tester, Consumer<Person> block) {
    for (Person p : persons) {
        if (tester.test(p)) {
            block.accept(p);
        }
    }
}

Cette fois, c’est l’interface Consumer<T> qui nous intéresse. C’est également une interface fonctionnelle, donc ne contenant qu’une seule méthode : void accept(T t). Cette méthode effectue un traitement sur un objet T sans rien renvoyer … ce qui est idéal pour un appel à la méthode printPerson() !

Donc on y va : dans notre classe Main, on va créer le Consumer<T> qui affiche une personne.

public static void main(String[] args) {
    Person p1 = new Person("Thomas", 19);
    Person p2 = new Person("Paul", 25);

    List<Person> persons = new ArrayList();

    persons.add(p1);
    persons.add(p2);

    Predicate<Person> plusVieuxQue = (p) -> {
        return p.getAge() >= 20;
    };
    Predicate<Person> plusJeuneQue = (p) -> {
        return p.getAge() < 20;
   };

   Consumer<Person> afficherPerson = (p) -> {
        p.printPerson();
    };
    processPersons(persons, plusVieuxQue, afficherPerson);
}

Et tant qu’à faire, on peut ajouter tous les Consumer que l’on veut …

Consumer<Person> afficherPerson = (p) -> {
    p.printPerson();
};
Consumer<Person> ajouterUnAn = (p) -> {
    p.setAge(p.getAge() + 1);
};

Il ne reste alors plus qu’à appeler la méthode processPersons(List, Predicate, Consumer) avec les combinaisons d’objets que l’on souhaite :

1
2
3
4
5
6
7
8
9
10
11
System.out.println("Qui a plus de 20 ans ?");
processPersons(persons, plusVieuxQue, afficherPerson);

System.out.println("Qui a strictement moins de 20 ans ?");
processPersons(persons, plusJeuneQue, afficherPerson);

System.out.println("Les plus jeunes que 20 ans grandissent d'un an ...") ;
processPersons(persons, plusJeuneQue, ajouterUnAn);

System.out.println("Et maintenant, qui a plus de 20 ans ?");
processPersons(persons, plusVieuxQue, afficherPerson);

Sur la console, on aura :

Qui a plus de 20 ans ?
Paul a 25 ans
Qui a strictement moins de 20 ans ?
Thomas a 19 ans
Les plus jeunes que 20 ans grandissent d'un an ...
Et maintenant, qui a plus de 20 ans ?
Thomas a 20 ans
Paul a 25 ans

Conclusion

Comme on le voit, on peut bien passer en paramètre des traitements qui rendent très générique la méthode associée, sans avoir à créer un grand nombre de classes implémentant ces traitements. C’est un des grands intérêts des lambdas : permettre de faire une programmation plus fonctionnelle. Et ces lambdas sont déjà présents partout dans le jdk 8 : il suffit de regarder par exemple les nouvelles méthodes disponibles pour les classes implémentant l’interface Collections (List, Map, …) !

Sources
http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

VN:R_U [1.9.22_1171]
Rating: 0 (from 0 votes)
Share
Ce contenu a été publié dans Java. Vous pouvez le mettre en favoris avec ce permalien.

Une réponse à Prise en main des lambdas expressions dans Java 8

  1. fgouget@excilys.com dit :

    Si je ne m’abuse, on peut encore alléger, et écrire:

    1
    Predicate<Person> plusVieuxQue = p -> p.getAge() >= 20;
    VN:R_U [1.9.22_1171]
    Rating: 0 (from 0 votes)

Laisser un commentaire