Le Touilleur ExpressLe Touilleur ExpressLe Touilleur ExpressLe Touilleur Express
  • Accueil
  • A propos de l’auteur
  • A propos du Touilleur Express

Les Closures enfin expliquées par un gars qui n'y connaît rien

    Home Java Les Closures enfin expliquées par un gars qui n'y connaît rien

    Les Closures enfin expliquées par un gars qui n'y connaît rien

    Par Nicolas Martignole | Java | 6 commentaires | 2 décembre, 2009 | 0 | 6 418 affichages
         

    Les Closures…

    Ce terme claque comme un vieux chewing-gum américain dans chacune de nos conversations de Geeks. C’était le mot à placer dans une conversation à Devoxx 2009 avec le JDK7. Alors voyons un peu de quoi il s’agit. J’en profite aussi pour présenter la version Groovy, avec un peu de code. Je me suis inspiré du livre Groovy in Action pour vous expliquer les Closures en Groovy, ce qui permet de comprendre leurs utilités en Java.

    Prenons un exemple simple en Java, pour tout d’abord expliquer quelques principes. Vous avez certainement déjà utilisé la méthode sort de la classe java.util.Collections non ? Cette méthode statique est capable d’effectuer un tri sur une liste en utilisant un comparateur. Imaginons un instant que je sois Georges. Il souhaite ranger ses capsules Nespresso par ordre alphabétique. Voyez ce que cela donne, avec un tri insensible à la casse :

    public class ClosureTouilleurExpress {
    
        public static void main(String args[]) {
            System.out.println("What else ?");
    
            List coffeeCaps = Arrays.asList( "Ristretto", "Arpeggio", "Roma",  "Cosi", "Volluto", "Livanto", "Indriya");
            Collections.sort(coffeeCaps, new Comparator() {
                public int compare(String s1, String s2) {
                    return s1.compareToIgnoreCase(s2);
                }
            });
            System.out.println("Mes capsules Nespresso par ordre alphabetique: " +        coffeeCaps);
    
        }
    
    }
    

    L’exécution et le résultat:

    What else ?
    Mes capsules Nespresso par ordre alphabetique: [Arpeggio, Cosi, Indriya, Livanto, Ristretto, Roma, Volluto]
    

    En Java il n’est pas possible de passer une référence vers une méthode, mais il est possible de définir une class anonyme, comme je viens de le faire ici. Il y a cependant une limitation: je ne peux pas passer plusieurs méthodes à ma méthode sort. Il est possible de s’en sortir en définissant une interface, implémentée par un object distinct, et ensuite passer cette interface à la méthode sort… C’est un peu compliqué non ?

    Je vais vous raconter la fin du film. Une Closure en Java une fois compilée, sera en fait une classe avec une seule méthode « invoke » static. Cette classe sera générée par le compilateur Java. Ne soyez pas trop triste, c’est la vie. Java ne touche pas au bytecode de la JVM. C’est un artifice d’écriture. Groovy de même. Donc tout ce que vous voyez vise à simplifier notre travail d’écriture, mais ne fera pas aller plus vite votre application. Pensez-y lors de vos dîners en ville. Closure en Java = raccourci pour exprimer un besoin, celui de travail en fonctionnel.

    Groovy
    Groovy propose déjà les Closures, et je trouve cela plutôt simple. Reprenons notre exemple et transformons-le en Groovy:

    List coffeeCaps =["Ristretto", "Arpeggio", "Roma",  "Cosi", "Volluto", "Livanto", "Indriya"]
    coffeeCaps.sort { String param1, String param2 -> param1.compareToIgnoreCase(param2) }
    println "Mes capsules Nespress par ordre alphabetique: " + coffeeCaps
    
    

    Plus simple que la version en Java non ?

    Si vous voulez tester ce script, vous pouvez même faire un copier-coller et lancer la console Groovy, hébergée sur Google App Engine et développée par Guillaume Laforge.

    La méthode sort est implicitement disponible sur les Lists en Groovy. Lorsqu’une méthode est appelée sans arguments, il n’est pas nécessaire de préciser les parenthèses (sort au lieu de sort()). La construction d’une liste en Groovy aussi est simple, on faisait cela en Perl il y a 10 ans déjà. Enfin le plus intéressant c’est la fonction de tri. La voilà notre fameuse Closure !

    La syntaxe est la suivante:

    { <arguments> -> <body de la closure> }
    

    Les arguments peuvent être typés (String p1, String p2) ou non (p1, p2). Le corps d’une closure est du code classique, similaire à une méthode anonyme en fait. Lorsqu’une Closure n’a pas d’argument, un object it est créé implicitement. Imaginons que vous souhaitez afficher le contenu de notre liste. Il suffit pour cela d’appeler la méthode each() et de passer une closure en argument.
    Ajoutons une ligne de Groovy dans la console:

    List coffeeCaps =["Ristretto", "Arpeggio", "Roma",  "Cosi", "Volluto", "Livanto", "Indriya"]
    coffeeCaps.sort {  param1,  param2 -> param1.compareToIgnoreCase(param2) }
    
    coffeeCaps.each{ println it } // Ici la closure n'a pas d'argument, le signe -> n'est pas indispensable
    ​
    

    Et exécutons ensuite le tout:

    Arpeggio
    Cosi
    Indriya
    Livanto
    Ristretto
    Roma
    Volluto
    

    La méthode each() de la Liste appelle la méthode de la closure pour chacun des éléments de la liste. L’itération de la liste donne un curseur (it) que j’utilise pour afficher directement la liste. Simple non ?

    Une Closure en Groovy finalement c’est un objet, que l’on peut passer en argument d’une méthode, affecter à une instance, bref aussi facile à manipuler qu’un Object classique. Dans l’exemple ci-dessous, j’ai créé un objet Closure avec l’algo de tri par ordre alphabétique. Regardez comment la méthode sort() prend en argument cette Closure. Je vous laisse imaginer les possibilités que cela vous offre.

    Closure rangeCapsule = { String p1,  String p2 -> p1.compareToIgnoreCase(p2)}
    
    List coffeeCaps =["Ristretto", "Arpeggio", "Roma",  "Cosi", "Volluto", "Livanto", "Indriya"]
    coffeeCaps.sort(rangeCapsule) //je passe la closure en argument de la methode sort()
    
    coffeeCaps.each{ println it }
    
    ​

    Est-ce que Groovy a réellement des Closures ? D’un point de vue technique, les Closures deviennent des Classes, qui seront ensuite compilées. Une Closure n’est jamais une méthode en Groovy. Mais le principe est vraiment pratique. Pour vous aider à comprendre par rapport à Java, imaginez en fait une class avec une méthode static, comme cet exemple du site Groovy:

    public class MyMath {
       public static int square(int numberToSquare){
            return numberToSquare * numberToSquare;
       }
    }
    ...
    ...
    int x, y;
    x = 2;
    y = MyMath.square(x); // y will equal 4.
    

    La même chose en Groovy devient:

    
    // Exemple complet
    Closure test={ numberToSquare -> numberToSquare * numberToSquare }
    println test(2) // affiche 4
    
    

    Et Java ?
    Mark Reinhold’s a publié un billet sur son blog, et il semble qu’il n’y ait plus que 3 propositions pour ajouter le concept de Closure au langage Java dans le JDK7 prévue pour 2010 ou 2011. Les 3 propositions retenues sont CICE, FCM et BGGA. Une série d’articles très bien écrits sur Developez.com vous donne une présentation détaillée de chacune des propositions.

    CICE (spec), par Bob Lee, Doug Lea et Josh Bloch.
    FCM (spec), par Stephen Colebourne et Stefan Schulz.
    BGGA (spec), par Gilad Bracha, Neal Gafter, James Gosling et Peter von der Ahé.

    BGGA
    La proposition BGGA est la plus complète. Elle est parfois un peu complexe à comprendre, mais le principe est de déclarer d’une part les paramètres d’entrées/sorties, et ensuite de spécifier l’algo.

    L’exemple de la fonction « sum » pour réaliser une addition se décomposerait de cette façon:

    { int, int => int }  additionne = { int x, int y => x+y };
    

    L’usage de la closure type BGGA serait alors:

    int resultat=additionne.invoke(2,5);
    // resultat vaut 7
    

    La closure est définie en 3 zones.
    1) Une zone où l’on spécifie le type de paramètres en entrées, le type retourné

    { int,int => int}
    

    2) le nom de la closure

    additionne
    

    3) la définition de la closure

     { int x, int y => x+y }
    

    La version FCM est différente, voici ce que cela donne:

    #(int(int,int)) plus = #(int x, int y) { return x + y; }
    

    Allez lire l’excellent article d’adiGuba sur Developpez.com

    FCM
    La plus grosse différence est l’utilisation du caractère # (dash) qui n’est pas utilisé en Java. Quelque part, cela pourrait nous aider à repérer une Closure rapidement, pourquoi pas ?

    CICE
    L’implémentation se base sur la simplicité. Assez différente des 2 autres, la proposition permet d’alléger du code classique. Voyez l’exemple de tri des éléments d’une liste par taille :

     List ls = ... ;
        Collections.sort(ls, new Comparator() {
            public int compare(String s1, String s2) {
                return s1.length() - s2.length();
            }
        });
    

    Avec la proposition CICE:

       List ls = ... ;
        Collections.sort(ls,
            Comparator(String s1, String s2){ return s1.length() - s2.length(); });
    

    Revenons à Groovy
    Et juste pour le fun, voici l’implémentation en Groovy:

    List coffeeCaps =["Ristretto", "Arpeggio", "Roma",  "Cosi", "Volluto", "Livanto", "Indriya"]
    
    coffeeCaps.sort { String p1, String p2 -> p1.length() <=> p2.length() }
    
    println "Par ordre croissant: " + coffeeCaps
    
    

    Ce qui donne:

    Par ordre croissant: [Roma, Cosi, Volluto, Livanto, Indriya, Arpeggio, Ristretto]
    

    Conclusion
    Je vous ai présenté le principe des Closures en me basant sur Groovy, dont la syntaxe est simple. J’avoue que j’ai un peu peur de ce que nous sommes entrain d’essayer de faire au langage Java en lui-même.

    Les Closures n’étaient pas nécessaires jusqu’à maintenant en Java. Simplement, les vieux développeurs s’ennuient. Et comme ils sont nombreux et qu’ils tiennent à ce que l’on se souvienne d’eux, ils tentent de proposer des innovations et des idées. Certaines sont bonnes, d’autres sont discutables. Je pense que les Closures sont une bonne idée, mais je suis plutôt circonspect quant à sa bonne utilisation par un junior devant son écran… J’ai peur. Réflexe de vieux.

    Lorsque le problème survient, avec son domaine d’application, je pense qu’il est plus intéressant d’apprendre un autre langage qui tourne sur la JVM comme Scala ou Groovy, plutôt que d’attendre un hypothétique consensus sur les Closures. Alors oui, il y aura des Closures en Java, la syntaxe sera simple ou non, mais je préfère maintenant investir mon temps à apprendre Scala et Groovy.

    Articles similaires:

    Default ThumbnailLe gars qui connaît Scala et celui qui débute Default ThumbnailComment trier un tableau en une ligne de code Default ThumbnailScrum ne fait rien par Tobias Mayer Default ThumbnailString et intern() on ne sait pas tout sur les Strings
    No tags.
    • Avatar
      Alexandre Bertails 3 décembre 2009 at 5 h 39 min

      Je trouve dommage qu’un fois de plus, on ne résume les closures qu’à du sucre syntaxique pour définir et/ou passer une fonction en paramètre.

      Une closure est une fonction qui admet des variables libres, c’est-à-dire non déclarée dans celle-ci. La résolution de ces variables dépend du contexte (on parle de capture de contexte) et c’est *là* que ça devient vraiment intéressant. Ça a le goût et la texture d’un langage dynamique m ais il n’en ai rien : on peut parfaitement compiler la fonction une bonne fois pour toute, il faut savoir où on trouvera la variable capturée lors de l’exécution… En Java, on peut émuler partiellement ce comportement mais au prix d’une complexité qui décourage de s’y mettre.

      Je n’ai personnellement pas pris le temps de suivre les rumeurs pour Java 7 mais à regarder cette synthèse [1] et le « Peut-être pas » en face de « Accès aux variables locales non-final » ainsi que les différents liens sur les différentes propositions permettent de dire que les « vraies closures » dans Java, c’est pas pour demain…

      En Scala, on ne s’en rend pas compte mais il y a des closures partout. Je viens de regarder dans Groovy : c’est bon, y a pas arnaque sur la marchandise 🙂

      Alexandre.

      [1] http://blog.developpez.com/adiguba/p8396/java/7-dolphin/info-closures-java-7/

    • Avatar
      Nicolas Martignole 3 décembre 2009 at 7 h 56 min

      @Alexandre : j’avais prévenu dès le titre que je n’y connaissais rien. J’ai fait le choix de passer par le code, pour parler de manière concrète à un développeur qui est entrain de me lire en trempant son exemplaire du Touilleur Express dans son café du matin. Pour la partie théorique, les articles sur Wikipedia par exemple sont bien faits. J’ai repensé à ta présentation au Paris JUG. J’imagine comment les Closures sont implémentées en Groovy et en Scala, mais je serai intéressé par un article sur « comment cela fonctionne » derrière, au niveau de la JVM. J’imagine une solution assez simple. En tous les cas merci pour le complément. Les commentaires sont aussi importants que l’article et permettent à tous de participer. A bientôt au Paris JUG.

    • Avatar
      Christophe Furmaniak 3 décembre 2009 at 10 h 31 min

      @Nicolas: et si tu installais le plugin WP « Syntax Highlighter and Code Prettifier Plugin for WordPress », ça rendrait le code de tes exemples ‘achement plus lisible ([mode fan= »on »]et ton blog serait encore plus mieux :)[/mode])

    • Avatar
      Nicolas Martignole 4 décembre 2009 at 13 h 09 min

      @Christophe c’est fait. en fait j’utilisais le plugin Google syntax highlighter mais il ne marche pas avec ce nouveau style. J’ai pris celui que tu m’as conseillé. Merci !

    • Avatar
      Dominique De Vito 4 décembre 2009 at 13 h 24 min

      Perso, ce que j\’aimerais, c\’est un auto-boxing des méthodes en objets implémentant une interface, comme je l\’indique ici : http://www.jroller.com/dmdevito/entry/next_jdk_wishlist_3_a

      Cela couterait pas cher et cela permettrait d\’améliorer un peu les choses.

      En gros, si j\’ai une méthode :

      Object myProcessing(Object e) {
      ...
      }

      Méthode dont la signature est compatible avec l\’interface :

      public interface UnaryOperator {
      Object eval(Object arg);
      }

      avec, par ex, l\’utilisation suivante de cette interface :

      public class AbstractList ... {

      public void map(UnaryOperator op) {
      ...
      }
      }

      Alors j\’aimerais pouvoir simplement écrire :

      mylist.map(myProcessing);

      Avec auto-boxing automatique de \ »myProcessing\ » en un objet implémentant l\’interface UnaryOperator.

      Ce serait un plus, et une avancée qui pourrait obtenir un consensus plus facilement que les différentes propositions des closures.

    Recent Posts

    • GitHub Actions : le tueur de Jenkins ?

      Avouez-le : ce titre de blog est super racoleur. J’avais aussi pensé

      15 février, 2021
    • Comment recréer du lien social dans l’Entreprise avec des outils numériques en 2021

      Nous sommes en février 2021 pendant le 3ème confinement lié à la

      10 février, 2021
    • FizzBuzz en Java et Scala (surtout Scala)

      L’exercice FizzBuzz est un petit exercice très simple, à tester par exemple

      9 février, 2021

    Recent Tweets

    •  @steeve  Excellente idée 😎👍🏻

      47 minutes ago
    • RT  @steeve : Si tu as déjà effacé une DB en prod, testé des règles iptables via ssh ou passé un crash loop sur une app iOS, viens nous racon…

      47 minutes ago
    • J'ai refais/modernisé l'authentification OAuth2 pour Google, Github et LinkedIn sur le CFP de Devoxx FR.

      4 hours ago
    •  @LostInBrittany   @FGRibreau   @aheritier  😎

      23 hours ago
    •  @LostInBrittany   @FGRibreau   @aheritier  J ai un souci GitHub demain à corriger aussi avec oauth2

      23 hours ago

    Mots clés

    agile (18) ajax (11) Apple (11) architecture (6) barcamp (5) BarCampJavaParis (5) ddd (5) devoxx (33) esb (6) exo (6) flex (9) geek (5) google (11) grails (5) groovy (10) humeur (12) humour (7) independant (6) iphone (12) Java (77) javascript (7) jazoon (28) jboss (22) jboss seam (12) jsf (9) jug (16) Linux (11) mac (6) mule (5) parisjug (7) paris jug (22) pjug (6) play (8) playframework (6) portlet (5) recrutement (6) ria (8) Scala (21) scrum (44) spring (23) Startup (11) usi (21) usi2010 (9) web (16) xebia (7)

    Le Touilleur Express

    Contactez-moi : nicolas@touilleur-express.fr

    Suivez-moi sur Twitter : @nmartignole

    Copyright© 2008 - 2020 Nicolas Martignole | Tous droits réservés
    • A propos de l’auteur
    • A propos du Touilleur Express
    Le Touilleur Express