Où va Java ? Les Closures (style CICE)

Les Closures style BGGA sont extrêmement complète, mais apporte également leurs lots de complexité. De ce point de vue là, la proposition CICE prend le problème à contre pied et propose une implémentation la plus simple possible.

D’ailleurs la documentation donne le ton dès le début, en titrant « Closures without Complexity« …


Sommaire / Accès rapide :


Closures ?

Oubliez les « functions types« , « method references » ou autres « structures de contrôle » de BGGA. CICE se veut plus simplet et le terme même de « closure » est lui-même peu utilisé dans la proposition.

En fait cela consiste simplement à proposer une syntaxe simplifiée pour la déclaration des classes anonymes, qui peut être utilisé pour :

  • Implémenter les interfaces mono-méthode (tout comme BGGA, les méthodes existantes dans Object ne sont pas prise en compte).
  • Étendre les classes-abstraites « mono-méthode-abstraite », c’est à dire ne possédant qu’une seule et unique méthode abstraite.

Ainsi, si on prend comme exemple le code suivant, permettant de trier une liste de chaînes :


List<String> list = ...
 
Collections.sort(list, new Comparator<String>() {
  public int compare(String s1, String s2) {
    return s1.compareToIgnoreCase(s2);
  }
});

La version CICE se contente d’alléger l’écriture en supprimant les informations rébarbatives, ce qui nous donne :


Collections.sort(list, Comparator<String>(String s1, String s2) {
  return s1.compareToIgnoreCase(s2);
});

Le principe consiste à omettre le mot-clé new (afin de bien différencier cette syntaxe de celle de création standard de classe anonyme), et de limiter les informations au stricte minimum : le nom de la classe/interface (et son éventuel paramétrage générique), ainsi que la liste des paramètres de la méthode à implémenter (qui est directement placée après le nom de la classe).

On supprime donc principalement une partie de la déclaration de la méthode, puisque selon le principe de la « mono-méthode » le compilateur peut facilement retrouver ces informations et vérifier leurs cohérence (c’est à dire que le retour du bloc correspondent bien au retour de la méthode).

Bref, il s’agit plutôt de sucre syntaxique permettant d’alléger la déclaration de classe anonymes, et de ce fait elle est parfaitement compatible avec les API existantes sans avoir besoin de conversions spécifiques (ici une closure est une classe anonyme comme une autre sans aucune spécificité).

Accès aux variables locales

CICE propose également une solution pour simplifier l’accès aux variables locales depuis une classe anonyme, afin de passer outre les limitations dû à l’utilisation de final, ceci via deux propositions :

  • Le mot-clé final devient implicite pour toute variable locale qui est initialisée ou assignée exactement une fois, ainsi que pour tout paramètre qui n’est pas réassigné dans le corps de la méthode. En clair toute variable qui respecte les conditions d’une variable final le deviendrait implicitement. De ce fait, ces variables deviennent directement utilisables depuis les classes anonymes sans avoir à utiliser le mot-clé final. Encore une fois cela ne change pas grand chose : c’est tout simplement le compilateur qui se chargerait de rajouter ce mot-clé, et on conserve donc les mêmes limitations qu’avec le mot-clé final (impossibilité de réassigner la variable).
  • Enfin, de la même manière que BGGA avec son annotation @Shared, CICE propose la possibilité d’accéder directement à la variable locale (qui serait alors conservé dans le « heap »), et donc de lui assigner une autre valeur.

    Ici, plutôt qu’une annotation, on utiliserait le mot-clé public pour indiquer que la variable est complètement accessible par toutes les classes anonymes, par exemple :

    
    
    public int numCompares = 0;
    Collections.sort(list, Comparator<String>(String s1, String s2) {
      numCompares++;
      return s1.compareToIgnoreCase(s2);  
    });
    System.out.println("Nombre de comparaison : " + numCompares);

Structures de contrôles

Contrairement à BGGA, CICE ne met pas en place de mécanisme de structures de contrôles. Par contre on l’associe avec la proposition ARM qui définit deux nouvelles structures du langage

La première instruction (do(...) {}) a pour objectif de faciliter la gestion des ressources (qui ne peuvent être géré par le garbage collector). Elle permet donc de déclarer un bloc qui s’occupera automatiquement de libérer proprement les ressources :


do (BufferedInputStream bis = ...) {
  ... // Code utilisant "bis"
}

Ce qui serait équivalent à :


BufferedInputStream bis = ...;
try {
  ... // Code utilisant "bis"
} finally {
  bis.close();
}

Tout en permettant l’utilisation conjointe de multiples ressources :


do (BufferedInputStream bis = ...; BufferedOutputStream bos = ...) {
  ... // Code utilisant "bis" et "bos"
}

La seconde instruction (protected(...) {}) propose une syntaxe permettant d’utiliser les lock de l’API java.ultil.concurrent d’une manière similaire au lock standard avec le mot-clef syncronized :


Lock lock = ...;
protected (lock) {
  ... // Code synchronisé avec le "lock"
}

Conclusion

« Closures without Complexity » : tel est le slogan de CICE. Mais en enlevant toutes la complexité des closures, on se retrouve bien loin des possibilités offertes par les autres propositions. Et s’il y a de bonne idée (j’apprécie particulièrement l’utilisation du mot-clé public pour rendre accessible une variable aux classes anonymes, en lieu et place de l’annotation @Shared de BGGA), l’intégration de CICE ne changerait pas vraiment le développement en Java, et serait en fait plutôt anecdotique : on est bien loin des possibilités offertes par les autres propositions…


Sur le même thème : Où va Java ? Les Closures

7 réflexions au sujet de « Où va Java ? Les Closures (style CICE) »

  1. Avatar de ®om®om

    Merci pour tes 2 articles sur les closures (BGGA et CICE), très complets.
    J’avais pour l’instant juste rencontré quelques propositions par-ci par-là, sans réellement comprendre leur fonctionnement. Maintenant c’est plus clair :)

  2. adiguba Auteur de l’article

    Uther : Les insertions des mot-clés assert et enum ont posé quelques soucis…

    Java n’assure pas de « forward compatibility », mais on distingue deux types de « backward compatibility » :

    • La compatibilité binaire, c’est à dire qu’un programme compilé avec une ancienne version de Java sera toujours fonctionnel sur une JVM plus récente. C’est le niveau de compatibilité qui est le plus assuré et rarement « cassé »…
    • La compatibilité des sources, c’est à dire qu’un programme qui compilait sans erreur avec un JDK plus ancien, doit pouvoir être compilé sans erreur avec un JDK plus récent.

    L’insertion d’un nouveau mot-clef ne pose aucun problème au niveau binaire, mais seulement au niveau des sources, car en tant que mot-clé il devient impossible de les utiliser comme nom de variable ou de fonction (ce qui pourrait être le cas dans les codes existants). Pour « régler » ce problème on peut compiler en utilisant l’option -source, mais cela empêche alors d’utiliser les évolutions du langages…

    Par contre les évolutions du langages posent généralement très peu de problème quand à la compatibilité avec les codes existants : je ne vois rien qui poserait un problème de compatibilité dans les propositions BGGA ou CICE…

    a++

  3. Avatar de UtherUther

    Ajouter un mot clé

    shared

    casserait seulement la forward compatibility. D’ailleurs utiliser

    public

    à l’interieur d’une méthode la casserait déjà elle aussi.
    Java ne garanti pas la compatibilité ascendante(forward compatibility), contrairement à la compatibilité descendante(backward compatibility). Et encore la forward compatibility n’est même pas totalement cassée vu qu’il y a le paramètre -source du compilateur est justement prévu pour gérer ça.
    Si je ne me trompe pas, elle à déjà été cassée au moins trois fois:
    – version 1.1 : inner classes anonymes
    – version 1.4 : assertions (mot-clé « assertion »)
    – version 1.5 : enumeration et générics/autoboxing (mot clé « enum »)
    S’il faut introduire de nouveaux mots clés, je pense au contraire que c’est le moment vu qu’il faudra de toute façon obligatoirement la casser un fois de plus si l’on veut introduire les closures, même pour la proposition CICE qui est la plus simple.

    Quant au mot clé

    volatile

    , il ne casserait même rien du tout, c’est pour ça que c’est mon préféré. Mais il faudrait voir si son utilisation ne rentrerait pas en conflit avec son utilisation actuelle. Il me semble que non, mais je ne suis pas assez calé pour en être certain.

  4. Avatar de djo.mosdjo.mos

    Uther> Pourquoi ne pas plustôt utiliser volatile ou shared mais en tant que mot clé et non annotation?

    Pour la sainte backward compatibility : si on introduit un nouveau mot clé comme shared dans le langage, des milliers et des milliers de projets ne compileraient plus … chose que Java a su éviter au fil des versions (hein .net ?)

  5. Avatar de UtherUther

    C’est surque niveau syntaxe, c’est on ne peut plus propre. Pas la peine de rajout de mot clé ni de wilcard. Le tout ce fait assez naturellement dans un bloc.
    Par contre c’est clairement très pauvre au niveau possibilités par rapport à ce que propose BGGA.

    Je suis partagé sur l’utilisation du mot clé public. Je le préfère également à une simple annotation et il peut paraitre correct dans le sens ou ont donne un droit d’utilisation supplémentaire à une variable.
    Mais sa signification est tout de même différente de celle habituelle. public, private et protected correspondent normalement à une problématique de visibilité.
    Dans ton exemple, numCompares ne change pas de visibilité, il est toujours visible que l’on le déclare public ou non. Du coup ça me dérange d’utiliser public pour cela.
    Pourquoi ne pas plustôt utiliser volatile ou shared mais en tant que mot clé et non annotation?

Laisser un commentaire