
Le projet Lambda, poussé par Oracle a peut-être engendrer un nouveau bébé, les "public defenders methods", dont l'objectif est de permettre de faire évoluer les interfaces Java.
De prime abord cela n'a aucun lien avec les expressions lambdas (ou "closures"), mais l'intérêt étant de pouvoir réellement enrichir l'API avec ces dernières. En effet les interfaces étant figées, il est assez difficile de faire évoluer l'API : le simple ajout d'une nouvelle méthode entraine de nombreuses incompatibilités.
Le rapport avec les expressions lambdas ? Ces dernières sont supposées simplifier et enrichir l'API, mais sont fortement limité par l'immuabilité des interfaces. Il fallait remettre cela en cause !
C'est là tout l'intérêt des "public defenders methods", qui, en permettant d'ajouter de nouvelles méthodes à des interfaces existantes, permettront de pouvoir utiliser au mieux les expressions lambdas.
Mais oublions les expressions lambdas pour se concentrer sur ce nouveau concept, et prenons un exemple concret et bien connu avec l'interface java.util.List et les méthodes Collections.sort().
Actuellement pour trier une List on utilise un code tel que celui-ci :
List<String> list = ... Collections.sort(list);
On se retrouve avec trois principaux défauts :
static, il n'y a aucune possibilité de redéfinir cette méthode. Or cela pourrait être extrêmement avantageux afin d'harmoniser le traitement de la méthode et l'implémentation. Par exemple, dans le cas présent on pourrait opter pour une méthode de tri plus adapté à notre structure de donnée.Les "public defenders methods" permettent de pallier à tous ces problèmes, en autorisant l'évolution des interfaces. En clair elle permettent de rajouter des méthodes "optionnelles" dans une interface existante en spécifiant l'implémentation qui sera utilisée par défaut lorsque la méthode n'est pas implémentée.
Dans notre cas, pour l'interface java.util.List cela donnerait donc quelque chose comme cela (notez-bien que la syntaxe n'est pas définitive) :
public interface List<E> extends Collection<E> { // Définitions des méthodes de base : ... // Ajout des nouvelles méthodes "optionnelles" extension void sort() default Collections.sort; extension void sort(Comparator<? super E> c) default Collections.<E>sort; }
Note : le paramétrage fortement typé de Collections.sort() pourrait poser des problèmes, mais c'est une autre histoire...
L'interface possède donc désormais deux nouvelles méthodes sort() :
list.sort().Ces méthodes définissent une implémentation par défaut, il n'est donc pas obligatoire de les implémenter (même si cela reste possible). Ce nouveau cas particulier implique deux modifications distinctes du compilateur et le la machine virtuelle, afin de "rajouter" les méthodes absentes si besoin.
Lorsqu'on compile une classe, le compilateur ajoutera une méthode "bridge" pour toutes les "defenders methods" non-implémentées. En clair le compilateur rajoutera lui-même le code de la méthode en se contentant d'appeler la méthode static, un peu comme si on avait soit-même saisie le code suivant :
public void sort() { Collections.sort(this); } public void sort(Comparator<? super E> c) { Collections.sort(this, c); }
Cela permet au code recompilé d'implémenter ces nouvelles méthodes même si elle ne sont pas réellement présente dans le code source, et donc de respecter les règles originales qui veut qu'une classe non-abstraite implémente toutes les méthodes de l'interface. Ainsi les méthodes optionnelles absentes sont ajoutées automatiquement par le compilateur.
Toutefois ce n'est pas suffisant, car cela impliquerait toujours l'obligation de tout recompiler en cas de modification d'une interface. En effet les classes compilées précédemment ne possèdent pas ces méthodes de bridges, ni d'implémentation des méthodes ajoutées par la suite. C'est là que rentre en compte la machine virtuelle, qui a l'avantage de pouvoir manipuler le code juste avant son exécution. En fait la JVM effectuera le même procédé que le compilateur, en injectant les méthodes bridges dans la classe lors de son chargement en mémoire.
Ainsi, si la classe a été compilé avec la dernière version de l'interface, le compilateur aura ajouté automatiquement les méthodes absentes, et elle sera alors à jour et correct. Sinon, la JVM se chargera de cela à l'exécution.
Reste à voir le problème que cela pourrait engendrer au niveau des ambiguïtés et des conflits de noms. Ce n'est pas réellement un nouveau problème puisque l'on le rencontre déjà lors de l'ajout d'une méthode dans une classe non-final, mais il est bon de le souligner.
Il y a également le retour sur le devant de la scène Java d'un problème qu'on croyait oublié : les conflits d'héritage multiple. Java évitait cela grâce à l'absence d'héritage multiple, mais on peut se retrouver dans un cas similaire si on implémente deux interfaces proposant chacune la même "defender method"...
Mais cela s'avère malgré tout très intéressant et relativement propre !
http://blog.developpez.com/htsrv/trackback.php?tb_id=8947
The use of the “extension” keyword (or similar, such as “optional”) is not even needed; the “default” clause (which is already a Java keyword) may be sufficient.
Vous devez être identifié pour poster un commentaire.

Ce blog est mis à disposition sous un contrat Creative Commons BY-NC-SA.
System.gc() a encore frappé : jre 1.5, tomcat 6.0 et multi processeurs
Tutoriels Java SE
Tutoriels Java EE
Sélection du blog
Java SE/EE et Web en général
| Lun | Mar | Mer | Jeu | Ven | Sam | Dim |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
Copyright © 2000-2012 - www.developpez.com