octobre
2006
Je comprend mieux l’opinion de Vincent Brabant sur la première proposition des closures. En effet je m’étais principalement orienté vers le coté syntaxique et tout l’intérêt que cela apporte pour la lisibilité et la simplification du code, sans vraiment faire attention à tout ce que cela impliquait (en relisant bien cette première version on s’aperçoit que cela apporte d’importante modification dans le langage, comme un nouveau type et des modifications dans l’API de réflection).
Or je viens de jeter un coup d’oeil à la seconde proposition de closures du dernier billet « en vrac » du blog Java : Concise Instance Creation Expressions: Closures without Complexity.
Comme son titre l’annonce cette proposition se veut bien moins complexe que son aînée…
Pour bien montrer les différences, je vais reprendre l’exemple du Comparator, dans un code basique qui permet de trier un tableau selon un attribut d’un objet, en utilisant une classe anonyme :
Arrays.sort(array, new Comparator<MyObject>() {
public int compare(MyObject o1, MyObject o2) {
return o1.getName().compareTo(o2.getName());
}
});
La version initiale propose ainsi d’écrire cela en utilisant à la place un type closure qu’il faudra définir spécifiquement :
Arrays.sort(array, (MyObject o1, MyObject o2) {
return o1.getName().compareTo(o2.getName());
});
Le problème de cette solution (en plus des impacts dans le langage), vient du fait que le paramètre de la méthode sort() doit être un type closure, ce qui implique la nécessitée de surcharger toutes les méthodes existantes pour qu’elles acceptent les closures. Ce qui peut être un impact important et un frein à leurs utilisation (il faudrait adapter toutes les API Java existante — standard ou non — afin de véritablement profiter des closures).
Le document proposait également une solution alternative qui consistait à encapsuler l’objet closure dans une classe anonyme de manière transparente pour le développeur… Mais cela impliquait la création de deux objets (la classe anonyme ET le type closure) là où un seul objet n’est réellement nécessaire…
Sucre syntaxique ?
Cette nouvelle approche n’a que très peu d’impact sur le langage et le bytecode généré, puisqu’elle ne consiste en soit qu’à une écriture plus succincte des classes anonymes. En effet, dans la majorité des cas, les classes anonymes sont utilisées avec des interfaces ou des classes qui ne possèdent qu’une seule et unique méthode abstraite. Et c’est précisément ce type de construction qui est destiné à être remplacé par les closures.
Ainsi, au lieu de définir un nouveau type dans le langage, on définit ici seulement une nouvelle façon de d’écrire une classe anonyme en redéfinissant l’unique méthode abstraite :
Arrays.sort(array, Comparator<MyObject>(MyObject o1, MyObject o2) {
return o1.getName().compareTo(o2.getName());
});
Cette syntaxe (nommée CICE) supprime un grand nombre d’information plus ou moins utile dans ce contexte, comme l’opérateur new
et la signature exacte de la méthode, alors que la déclaration des paramètres de la méthode sont accolé au nom de la classe/interface.
Malgré tout, cette syntaxe (si elle était acceptée), générerait exactement le même bytecode que le tout premier exemple qui utilise les classes anonymes de manière classique. Toutes les modifications n’affecteront que le compilateur (qui devra traité cette syntaxe), mais en aucun cas le bytecode généré ni la JVM, ce qui assure une compatibilité parfaite.
Même si cette solution est légèrement plus verbeuse que son aînée (elle comporte le nom de la classe/interface et ses éventuels paramètres typés), ses avantages en font sûrement un candidat viable pour la possible inclusion des closures dans Java SE 7…
Et même plus…
Autre point intéressant, le document propose également deux fonctionnalités supplémentaire (qui impliquent surement une modification du bytecode, au moins pour la seconde), ceci afin de faciliter un peu plus l’écriture des classes anonymes/CICE.
La première proposition consiste à rendre le mot-clef final
implicite lorsque la référence d’un paramètre ou d’une variable de la méthode parente est utilisée dans une classe anonyme (à condition bien entendu que cette référence ne soit pas modifié par la suite). Pour rappel, ce mot-clef permet d’interdire toute modification de la référence d’une variable, et donc de l’utiliser dans une classe anonyme. Les restrictions dûs à l’utilisation du mot-clef resteront bien sûr valable, et cela signifie seulement que c’est le compilateur qui se chargera de placer le mot-clef final
à votre place (ce que je trouve d’un intérêt plutôt limité puisque cela casse la compatibilité du code source sans pour autant apporter un réel avantage).
La seconde proposition est bien plus intéressante, puisqu’elle permet de combler une des lacunes du « partage » de référence entre une méthode et une classe anonyme. En effet, il est impossible pour une classe anonyme de modifier la référence (ou la valeur pour les types primitifs) d’une variable déclaré dans la méthode qui l’a créé, puisqu’il est nécessaire que cette variable soit déclaré final
.
Généralement, on utilise une astuce en utilisant un tableau à un élément : on partage le tableau entre la méthode et la classe anonyme, et on est libre de modifier la référence de l’unique élément du tableau comme bon nous semble (puisque seul la référence du tableau ne peut pas être modifié)
Par exemple, et toujours en reprenant le tout premier exemple, si l’on veut compter le nombre de passage dans la méthode de notre classe anonyme, on utiliserait actuellement le code suivant :
final int[] numCompares = new int[1];
Arrays.sort(array, new Comparator<MyObject>() {
public int compare(MyObject o1, MyObject o2) {
numCompares[0]++;
return o1.getName().compareTo(o2.getName());
}
});
System.out.println(numCompares[0]);
La proposition consiste à utiliser le mot-clef public
sur les variables locales, qui indiquerait à la fois que la variable peut être utilisée par les classes anonymes, mais que sa valeur/référence peut très bien être modifié autant de fois que nécessaire. Ainsi, on peut se passer de l’utilisation du tableau et modifier directement la variable, ce qui donnerait un code bien plus lisible :
public int numCompares = 0;
Arrays.sort(array, Comparator<MyObject>(MyObject o1, MyObject o2) {
numCompares++;
return o1.getName().compareTo(o2.getName());
});
System.out.println(numCompares);
Pour conclure…
Si syntaxiquement j’approuvais la première version de closures, je doit admettre que leurs complexités et leurs impacts sur le langage sont trop important. Cette seconde proposition est donc largement préférable à mon avis.
En ce qui concerne les modifications de l’accès aux variables locales, je suis à la fois méfiant (le final
implicite ne me plait guère) et intéressé (la « partage » de variable locale via le mot-clef public
) même s’il faudra bien prendre de temps de voir les impacts que cela pourrait entrainer…
Les closures risquent encore de faire beaucoup parler…
7 Commentaires + Ajouter un commentaire
Tutoriels
Discussions
- Définition exacte de @Override
- Possibilité d'accéder au type générique en runtime
- Difference de performances Unix/Windows d'un programme?
- L'apparition du mot-clé const est-il prévu dans une version à venir du JDK?
- Classes, méthodes private
- [REFLEXION] Connaitre toutes les classes qui implémentent une interface
- Recuperation du nom des parametres
- [ fuite ] memoire
- jre 1.5, tomcat 6.0 et multi processeurs
ps : note importante : je suis un débuteur moyen ^^ (voir débutant).
lol
Faudrait pas que j’m’la pète indumment !
++
ZedroS
ps : et encore merci pour ces blogs forts intéressants !
>> .Net et Java faisant la course aux fonctionnalités linguistiques alors que le développeur moyen doit en utiliser qu’une fraction…
Ce point de vue est intéressant !
D’un coté il y a une volonté de ne pas trop s’étendre dans tous les sens pour conserver la philosophie de Java : » Un langage simple au niveau syntaxique, mais avec une API très complète « .
En même temps il je pense qu’il est nécessaire de faire évoluer le langage et simplifier encore la vie des développeurs…
Ce n’est pas forcément un processus évident… mais une chose est sûr, l’arrivée de .NET aura accéléré ce processus…
a++
Course à l’armement, le terme est bien trouvé. On se trouve en effet dans une situation globalement similaire, .Net et Java faisant la course aux fonctionnalités linguistiques alors que le développeur moyen doit en utiliser qu’une fraction…
ca permettrait comme en javascript de passer une methode directement en argument de methode, et etre capable de l’utilise dans la methode ? c’est ce que je trouve le plus sympa dans ce genre de syntaxe.
Sinon, effectivement, je pense que la version 2 est bien meilleure parce que quand je vois le retard des developpeurs autour de moi pour encaisser java5, je me dis qu’il faut arreter la course a l’armement
>> Je me demande toujours dans quelle mesure ce genre d’avancée en est vraiment une.
Le but principal est de proposer une alternative aux classes anonymes et a leur syntaxe assez lourde. La première solution propose des méthodes anonymes comme un nouveau type, alors que la seconde solution se contente de décrire une syntaxe simplifié pour déclarer une classe anonyme…
L’objectif étant de rendre le code un peu plus lisible…
Sinon elle sont utilisée dans C# (mais je crois qu’elle était présente depuis l’origine du langage, ce qui doit poser moins de problème de compatibilité).
>> Est-ce qu’il existe un JSR pour les closures ?
Je ne pense pas… Pas pour le moment en fait…
>> Parce que en fait, normalement, lorsqu’il y a un JSR, le JCP veut que tout ce qui est dit dans ce JSR soit confidentiel.
Cela devrait changer avec la prochaine version du JCP : http://jcp.org/en/jsr/detail?id=306
Il y a des chances que ce soit valable pour Java SE 7
a++
Je suis content de voir que tu comprends mieux mon opinion.
Et je suis également content de voir la 2ième proposition.
Il y a tout de même un fameux progrès.
Question que je me pose:
Est-ce qu’il existe un JSR pour les closures ?
Parce que en fait, normalement, lorsqu’il y a un JSR, le JCP veut que tout ce qui est dit dans ce JSR soit confidentiel.
Et je suis en train de me demander si le fait que les closures s’améliorent comme cela au fil des versions, ce n’est pas justement grâce au fait qu’il n’y a pas encore de JSR pour les closures, et donc qu’on peut en débattre publiquement, et que finalement, c’est bien plus positif que de voir les final drafts, lorsqu’on est quasi à la fin du cycle, et que c’est un peu tard pour rectifier le tir.
Maintenant, je dis peut-être une bétise énorme.
Vincent
Les closures font aussi bien mal à la tête je trouve lol
Je me demande toujours dans quelle mesure ce genre d’avancée en est vraiment une.
Qu’en est il des autres langages? C# y a t il recours ? C++ ? Y a t il des retours sur leur fréquence d’usage ? Quand je vois le code pas lisible que l’on est déjà capable de pondre actuellement, sans user pourtant de classes anonymes et autres subtilités délicieuses, j’suis parfois inquiet.
ZedroS en soirée… fatigué !