
C'est fait, le projet Lambda est né de la volonté d'intégrer un système de closures, que l'on nommera désormais "expressions lambda".
On y retrouve donc une liste de diffusion mais surtout un prémice de la proposition : Project Lambda : Straw-Man Proposal.
On y retrouve bien sûr le détail des fonctionnalités de base dont j'ai déjà parlé plusieurs fois sur ce blog : les expressions Lambda, les types Fonction et les règles de conversion vers les types SAM.
D'autres sections restent vides, comme la transparence des exceptions ou les références de méthodes, mais sont bel et bien prévues dans la proposition (il s'agit toujours d'un document de travail).
Enfin, on dispose de plus d'information quand à l'accès aux variables, mais surtout on y découvre l'intégration des méthodes d'extensions !
Du coup j'en profite pour mettre à jour le tableau comparatif que j'avais publié dans un billet précédent :
| CICE | BGGA 0.5 | FCM 0.5 | CFJ 0.6a | Devoxx | Lambda | |
|---|---|---|---|---|---|---|
| Littéraux pour la réflection | NON | NON | OUI | NON | ? | ? |
| Référence de méthode | NON | NON | OUI | OUI | ? | prevu |
| Closures assignable aux interfaces mono-méthode | OUI | OUI | OUI | OUI | ? | OUI |
| Closures indépendante des interfaces mono-méthode | NON | OUI | OUI | OUI | OUI | OUI |
| Accès aux variables locales non-final | OUI | OUI | OUI | OUI | Peut-être pas | OUI |
| 'this' référence la classe englobante | NON | OUI | OUI | OUI | Probablement | OUI |
| 'return' lié à la closure | OUI | OUI* | OUI | OUI | OUI | OUI |
| 'return' lié à la méthode appellante | NON | OUI* | NON | NON | NON | NON |
| Transparence des exceptions | NON | OUI | OUI | OUI | Peut-être pas | prévu |
| Type 'Fonction' | NON | OUI | OUI | OUI | OUI | OUI |
| Structures de contrôles | NON | OUI | NON | NON | NON | NON |
| Méthodes d'extension | NON | NON | NON | NON | NON | OUI |
| * : La proposition BGGA 0.5 permettait au mot-clef return d'avoir deux significations différentes selon le contexte. | ||||||
C'est un détail d'implémentation, mais c'est confirmé : le mot-clef this référencera bien l'instance englobante et non pas l'instance de l'objet représentant l'expression lambda.
Petit rappel : actuellement dans une classe anonyme, on ne peut accéder qu'aux variables locales précédemment déclarées final, car on travaille en réalité sur une copie du type primitif ou référence de la variable.
Avec les expressions Lambda, ceci sera optionnel tant que la variable respectera bien les conditions du final. On pourra accéder à une variable tant que la variable n'est assignée qu'une seule fois avant la déclaration de l'expression lambda, et qu'elle n'est plus modifiée par la suite. En clair, on pourra y accéder tant qu'elle répond aux conditions de final, et cela revient ni plus ni moins à rendre le mot-clef final implicite : c'est le compilateur qui le mettra pour nous.
Toutefois la proposition ne s'arrête pas là puisqu'elle met en place un nouveau mot-clef (shared) qui permettra aux expressions lambda de partager l'accès à n'importe quelle variable locale en lecture/écriture.
L'exemple donné permet de comptabiliser le nombre de comparaisons effectuées pendant un tri :
shared int count = 0; // Déclaration de la variable partagée Collections.sort( data, #(String a, String b) { count++; return a.length() - b.length() } ); System.out.println(count)
La variable locale peut alors être modifiée à l'intérieur de l'expression lambda. Bien sûr, ces modifications se reportent alors dans la méthode appelante.
Pour info, contrairement à assert ou enum en leurs temps, l'ajout de ce mot-clef ne devrait pas poser de problème de compatibilité puisqu'il s'agit d'un mot-clef restreint au contexte.
Lors de l'annonce du retour des closures à la conférence Devoxx, il était également question des méthodes d'extension, mais je n'avais pas vraiment fait attention à cela. Il faut dire que j'avais vu plusieurs propositions dans ce sens qui ne m'avaient pas vraiment convaincu, car l'appel d'une méthode pouvait devenir un peu imprévisible... Mais cette proposition me semble nettement plus "propre".
Mais revenons-en à la source du problème : les interfaces ne peuvent pas être modifiées sans casser la compatibilité. En effet, la moindre modification dans une interface va forcément impacter toutes les implémentations potentielles, que ce soit dans l'API standard mais surtout dans toutes les applications existantes.
Du coup, une fois qu'une interface est utilisée, on ne peut plus y ajouter de nouvelle méthode sans casser la compatibilité, et on passe généralement via une méthode static dans une classe utilitaire, comme c'est généralement le cas avec la classe Collections pour les différentes interfaces de l'API de Collections.
Toutefois, l'utilisation de ces méthodes static est bien moins pratique qu'un appel de méthode standard. C'est là qu'interviennent les méthodes d'extension, en permettant d'appeler une méthode static comme s'il s'agissait d'une méthode d'instance.
Pour cela, il suffit d'ajouter la signature de méthode dans l'interface, en spécifiant via un import static le nom de la méthode qui sera exécutée par défaut lors de l'appel de la méthode.
Par exemple, si on souhaite modifier l'interface List pour y inclure la méthode sort(), il suffit de modifier l'interface de la manière suivante :
public interface List<T> extends Collection<T> { ... public void sort() import static Collections.sort; }
La méthode sort() devient de ce fait optionnelle : les classes qui implémentent l'interface List n'ont pas à l'implémenter, mais on peut l'appeler sur toutes les instances de la classe List :
List<String> list = ... list.sort();
Par défaut un tel appel serait converti en un appel équivalent vers la méthode statique Collections.sort(), soit :
Collections.sort(list);
Toutefois, si l'implémentation réelle de l'objet "list" possède une méthode sort(), c'est cette dernière qui serait alors exécutée par la JVM. Ce qui permet de conserver un mécanisme d'héritage malgré tout. La méthode statique servant en fait d'implémentation par défaut.
L'idée me semble vraiment intéressante, car cela permettra enfin de faire évoluer les sacro-saintes interfaces sans tout casser, tout en restant assez restreinte pour éviter les dérives. En clair cela reste à la charge du concepteur de l'interface et non pas du premier développeur venu, ce qui évitera les mauvaises utilisations, les abus et les confusions...
Reste à voir concrètement ce que cela va donner, et comment ce sera utilisé dans l'API standard.
http://blog.developpez.com/htsrv/trackback.php?tb_id=8451
public static boolean listEquals(List list, Object equals) {
// l'égalité des listes est spécifiée de manière particulière
}
public interface List {
public boolean equals(Object other) import static ListUtils.listEquals() ;
// ou queque chose comme ça avec la syntaxe qu'il faut ...
}
An extension method in an interface is hidden in a class implementing that interface if the class defines a static or instance method with the same signature.
hello() correspond-il à un appel de méthode ou à l'utilisation d'une expression lambda ?hello() correspondrait à l'appel de la méthode hello() de la manière la plus classique qu'il soit.hello!() permettrait d'utiliser l'expression lambda "hello".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 |
Copyright © 2000-2012 - www.developpez.com