Java 8 est disponible, la plate-forme se met aux expressions lambdas,
Tour d'horizon des nouveautés

Le , par Hinault Romaric, Responsable .NET
Si les versions 6 et 7 de Java étaient des évolutions douces : Java 8 est d'un tout autre ordre. Plus de 56 nouvelles fonctionnalités ont été ajoutées (http://openjdk.java.net/projects/jdk8/features). Les arrivées des lambdas, des méthodes par défaut, des interfaces fonctionnelles et de Stream vont modifier en profondeur le langage et donc l'écosystème Java tout entier. Nous pouvons aussi citer l'incorporation d'une nouvelle API pour gérer les dates, de nouvelles annotations et d’un nouveau moteur d'exécution JavaScript.


Java 8 devrait ainsi avoir un impact au moins aussi important que Java 5 à son époque (rappelez-vous l'apparition des Generics). Il faut donc s'y préparer dès à présent. Voici quelques nouveautés plus en détail.


Les nouveautés du langage



Interfaces fonctionnelles
: connues précédemment sous le nom de Single Abstract Method interfaces (SAM Interfaces), cette nouveauté introduit les interfaces qui possèdent uniquement une seule méthode d’instance abstraite. Les plus connues sont java.lang.Runnable, java.awt.event.ActionListener, java.util.Comparator. Avec Java 8, elles portent le nom d'interfaces fonctionnelles. Dès lors qu'une interface possède une seule méthode d’instance abstraite, elle est désignée comme interface fonctionnelle. Il est aussi possible d'annoter l'interface par @FunctionalInterface. Si une interface est annotée ainsi et possède plus d'une méthode d’instance abstraite, une erreur de compilation sera produite. C'est un peu le même principe qu'avec l'annotation @Override.

L'interface ci-dessous Runnable possède une méthode et est annotée @FunctionalInterface.
Code : Sélectionner tout
1
2
3
4
@FunctionalInterface 
public interface Runnable { 
    void run(); 
}
Le nouveau package java.util.function propose d’ailleurs un certain nombre d’interfaces fonctionnelles répondant à divers usages.

Lambdas : il s'agit de la plus grosse nouveauté de Java 8. Décrite depuis la JSR 335 (https://jcp.org/en/jsr/detail?id=335), cette fonctionnalité permet d'apporter la puissance de la programmation fonctionnelle dans Java. Une expression lambda peut être assimilée à une fonction anonyme, ayant potentiellement accès au contexte (variables locales et/ou d'instance) du code appelant. Ces "fonctions anonymes" peuvent être affectées dans une interface fonctionnelle. Le code de l’expression lambda servira ainsi d’implémentation pour la méthode abstraite de l’interface. On peut donc les utiliser avec n'importe quel code Java utilisant une telle interface, à condition que les signatures de la méthode correspondent à celle de l’expression lambda.

La syntaxe utilisée est la suivante : (paramètres) -> code ou (paramètres) -> {code} quand il y a plus d'une instruction.

Prenons l'exemple du tri des éléments d'une collection.
Code : Sélectionner tout
1
2
3
4
5
6
Arrays.sort(testStrings, new Comparator<String>() { 
    @Override 
    public int compare(String s1, String s2) { 
        return(s1.length() - s2.length()); 
    } 
});
En utilisant les lambdas, la nouvelle écriture sera :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
// Forme longue : 
Arrays.sort(testStrings, (String s1, String s2) -> { return s1.length() – s2.length(); }); 
 
// Forme courte (possible uniquement s’il n’y a qu’une instruction) : 
Arrays.sort(testStrings, (String s1, String s2) -> s1.length() – s2.length()); 
 
// Forme courte avec type implicite des paramètres 
// (le type est déduit par le compilateur via l’inférence) 
Arrays.sort(testStrings, (s1, s2) -> s1.length() – s2.length());
Les interfaces fonctionnelles servent aux lambdas, facilitant ainsi l'écriture puisqu'elles permettent d'écrire l'implémentation de façon plus concise. Nous montrons ci-dessous un exemple d'implémentation de l'interface Runnable.
Code : Sélectionner tout
Runnable r1 = () -> { System.out.println("My Runnable"); };
Plus de détails sur les lambdas sont disponibles dans un tutoriel publié récemment : http://soat.developpez.com/tutoriels...t-lambda-java8

Références de méthode : une référence de méthode est utilisée pour définir une méthode en tant qu’implémentation de la méthode abstraite d’une interface fonctionnelle. La notation utilise le nom de la classe ou une instance de la classe, suivi de l'opérateur « :: » et du nom de la méthode à référencer. Le type des paramètres sera déduit du contexte selon l’interface fonctionnelle vers laquelle on affecte la référence.

On peut distinguer quatre types de méthodes références :
  • Les références vers une méthode static, qui s’utilisent toujours avec le nom de la classe en préfixe. La signature de la référence correspond alors à la signature de la méthode.
    Code : Sélectionner tout
    1
    2
    Supplier<Double> random = Math::random; 
    double result = random.get(); // Math.random();
  • Les références vers une méthode d’instance, liées à une instance spécifique, qui s’utilisent toujours avec l’instance en préfixe. Ici également, la signature de la référence correspond à la signature de la méthode, et tous les appels s’appliqueront sur l’instance définie dans la référence de méthode :
    Code : Sélectionner tout
    1
    2
    3
    Random r = new Random(); 
    Supplier<Double> random2 = r::nextDouble; 
    double result2 = random2.get(); // r.nextDouble();
  • Les références vers une méthode d’instance, mais sans lien avec une instance précise. Comme pour les méthodes static, on utilisera comme préfixe le nom de la classe. La signature de la référence correspond alors à la signature de la méthode, précédée par un argument du type de la classe, qui correspondra à l’instance sur laquelle on appellera la méthode :
    Code : Sélectionner tout
    1
    2
    3
    4
    5
    6
    7
    Function<Random,Double> random3 = Random::nextDouble; 
    Random r1 = new Random(); 
    Random r2 = new Random(); 
    Random r3 = new Random(); 
    double result1 = random3.apply(r1); // r1.nextDouble(); 
    double result2 = random3.apply(r2); // r2.nextDouble(); 
    double result3 = random3.apply(r3); // r2.nextDouble();
  • Enfin, il est possible de référencer un constructeur en utilisant le mot-clef “new” comme nom de méthode. Très pratique pour créer une fabrique :
    Code : Sélectionner tout
    1
    2
    Function<String, Thread> factory = Thread::new; 
    Thread t = factory.apply("name"); // new Thread("name");



Les références de méthodes sont une alternative aux expressions lambdas, lorsqu’il n’y a qu’une seule et unique méthode à exécuter, pour une syntaxe encore plus claire :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
Random r = new Random(); 
 
Supplier<Double> random = Math::random; 
Supplier<Double> random2 = r::nextDouble; 
Function<Random,Double> random3 = Random::nextDouble; 
Function<String, Thread> factory = Thread::new; 
 
Supplier<Double> random = () -> Math.random(); 
Supplier<Double> random2 = () -> r->nextDouble(); 
Function<Random,Double> random3 = (Random random) -> random.nextDouble(); 
Function<String, Thread> factory = (String name) -> new Thread(name);
Cela peut également être une alternative intéressante à l’API de reflection, puisque cela permet un code sécurisé.

Méthodes par défaut (Defender Methods) : cette fonctionnalité permet de proposer une implémentation dite par "défaut" aux méthodes déclarées dans les interfaces. Par conséquent, depuis Java 8, une interface Java contient du code. L'avantage est de pouvoir faire évoluer les interfaces sans avoir à tout casser.

Dans l'exemple ci-dessous, une interface Person déclare deux méthodes. La méthode sayHello est dite par défaut via le mot clé default. Toute implémentation de Person imposera que la méthode sayGoodBye() soit implémentée. Pour sayHello, l'implémentation ne sera pas obligatoire, même si elle reste bien sûr possible.
Code : Sélectionner tout
1
2
3
4
5
6
interface Person { 
    void sayGoodBye(); 
    default void sayHello() { 
        System.out.println("Hello there!"); 
    } 
}
Les méthodes par défaut permettent ainsi de faire évoluer l’API des interfaces sans provoquer de grosses incompatibilités dues à l’absence d’implémentation dans les classes qui les implémentent. L’API de base en profite grandement en enrichissant certaines de ses interfaces (en particulier dans l’API de Collections dont les interfaces s’enrichissent de plusieurs méthodes).

Plus de détails sur cette nouveauté peuvent être trouvés sur le tutoriel publié dernièrement : http://oliviercroisier.developpez.co...es-interfaces/

Méthodes static dans les interfaces : Java 8 propose également la possibilité de créer des méthodes statiques depuis une interface Java.

Dans l'exemple ci-dessous, une interface Person déclare une méthode statique.
Code : Sélectionner tout
1
2
3
4
5
interface Person { 
    static void sayHello() { 
        System.out.println("Hello there!"); 
    } 
}
Cela peut s’avérer utile pour proposer des méthodes utilitaires liées à l’interface (comme une “fabrique” par exemple).

Les mises à jour de l'API

Stream et parallèles streams sur les collections : Java 8 apporte également la notion de Stream, qui représente un flux de données que l'on peut manipuler à la volée. L'utilisation d'un Stream se compose de trois parties :
  • la création du flux à partir d'une source de données qui peut être très variée (un tableau, une collection, un flux d'entrée/sortie, des données générés à la volée, etc.)*;
  • des opérations intermédiaires, qui permettent de transformer le flux en un autre flux (en filtrant des données ou en les transformant par exemple)*;
  • une opération terminale, qui permet d'obtenir un résultat ou d'effectuer une opération spécifique.


Par exemple, pour créer un flux à partir d'une collection, on utilisera tout simplement la nouvelle méthode stream() de Collection.
On peut alors appliquer autant d'opérations intermédiaires que nécessaire, comme filter(), qui permet de filtrer certaines données, map() qui permet de modifier la donnée à la volée, distinct() qui permet d'éviter les doublons, sorted() qui permet de les trier, ou encore limit() qui restreint la taille des données.
Une fois toutes nos opérations intermédiaires effectuées, il faut alors appliquer la méthode finale qui va déterminer notre action. On peut par exemple utiliser forEach() pour itérer sur toutes ces valeurs, min()/max() pour récupérer seulement la valeur min/max, finAny()/findFirst() pour récupérer un élément correspondant aux critères, etc.

Bien sûr, toutes ces opérations sont pensées pour être utilisées avec des expressions lambdas ou des références de méthodes.

API java.time (http://openjdk.java.net/jeps/150) : la nouvelle API date, heure et calendrier basée sur la JSR 310 est disponible dans les packages java.time.*. Les classes sont désormais immuables et thread-safe. L'utilisation des méthodes chaînables rendent le code plus lisible. La nouvelle API différencie également deux modèles de temps : le temps machine et le temps humain. Pour une machine, le temps n’est qu’un entier augmentant depuis l’epoch (01 janvier 1970 00h00min00s0ms0ns). Pour un humain en revanche, il s’agit d’une succession de champs ayant une unité (année, mois, jours, heure, etc.). À noter également que le mois "0" n'existe plus et le mois "1" correspond au mois de janvier. Plus de détails sur cette nouveauté peuvent être trouvés sur le tutoriel publié dernièrement : http://soat.developpez.com/tutoriels...me-date-java8/.

Annotations multiples (http://openjdk.java.net/jeps/120) : Java 8 offre maintenant la possibilité de répéter plusieurs fois une annotation de même type pour un élément donné d'un programme.

Dans l'exemple ci-dessous, l’annotation @Schedule est utilisée deux fois, ce qui n'était pas permis dans les versions antérieures.
Code : Sélectionner tout
1
2
3
@Schedule(dayOfMonth="last") 
@Schedule(dayOfWeek="Fri", hour="23") 
public void doPeriodicCleanup() { ... }
Nashorn, le nouveau moteur Javascript (http://openjdk.java.net/jeps/174) : un moteur JavaScript proposé par défaut depuis le JDK. Plus de détails sur cette nouveauté peuvent être trouvés sur le tutoriel publié récemment : http://soat.developpez.com/tutoriels...nashorn-java8/

Nous pourrions continuer la liste des modifications en précisant également la suppression du PermGen pour la machine virtuelle, les nombreux ajouts pour JavaFX (http://pixelduke.wordpress.com/2013/...part-i-javafx/) ou finalement l'ajout de classes dont l'intérêt va se découvrir au fil du temps. L'on peut citer, par exemple, les classes LongAdder et DoubleAdder (http://blog.palominolabs.com/2014/02...vs-atomiclong/).

Vous l'aurez compris, Java 8 nous apporte une vraie évolution dans le paysage Java. Pour plus de détails, n'hésitez pas à faire un tour sur :


Les avis de l'équipe Java de Developpez.com

Fabrice Bouyé (bouye)

Le JDK 8 est enfin là !

À la vue des expressions lambdas, certains diront qu'avec quelques années de retard, Java met enfin à niveau sa syntaxe événementielle pour rattraper celle de C#, Groovy ou encore Scala.
Cependant, on aurait tort de penser qu'il s'agit là d'un simple sucre syntaxique destiné à donner un aspect moderne au langage et à le rendre plus attrayant !

Ces nouvelles fonctionnalités arrivent, en effet, avec une refonte majeure de la JVM, que ce soit au niveau du support des expressions lambdas elles-mêmes, des références de fonctions (similaires aux pointeurs de fonctions pour ceux ayant fait du C/C++) ou encore de la refonte des collections par l'introduction des streams.
Outre l'aspect extérieur (les API que nous, programmeurs, sommes amenés à manipuler), désormais la JVM s'adapte aux traitements en parallèle et au support du multicœur, et le tout sans devoir manuellement manipuler des threads ou des workers !

De plus, l'excellente bibliothèque des gestions du temps JODA-Time a fini par évoluer pour devenir le JSR 310, la nouvelle API de temps et de date du JDK 8.
Simple, efficace et pratique d'emploi, elle promet de nous faire rapidement oublier les fastidieuses opérations sur java.util.Calendar et java.util.Date, toujours promptes à créer toutes sortes de fiascos temporels si on n'y prend pas garde.

J'attends beaucoup de ce premier pack de fonctionnalités. Cependant, je garde également l’œil ouvert sur ce qui est encore à venir, ces autres fonctionnalités qui n'ont pu être intégrées au JDK 8 (jigsaw, etc.).
Les plans pour le futur JDK 9 semblent toujours aussi appétissants...

Mickael Baron (Mickael Baron)

Je suis très enthousiaste de l'arrivée de Java 8 et des nombreuses fonctionnalités qui sont proposées. À mon avis, il faudra un certains temps afin de maîtriser toutes les subtilités des améliorations. Je compte sur les nombreux rédacteurs de Developpez.com pour nous faire découvrir tous les secrets de cette version.

Je suis également curieux de voir comment cette nouvelle version va s'imposer dans le monde de l'entreprise où on croise encore des JDK 1.4.

La fonctionnalité la plus intéressante de mon point de vue reste les implémentations par défaut, je regrette même qu'elle arrive aussi tard. Les évolutions des interfaces seront plus faciles à réaliser. Pour les lambdas, je ne suis pas encore assez expérimenté pour en comprendre toutes les subtilités mais je compte bien m'y mettre quand mon Eclipse préféré sera disponible.

Bienvenue Java 8 !!!

Frédéric Martini (AdiGuba)

Java continue son évolution.

D'aucuns diront qu'il s'agit d'une évolution bien lente. Je parlerais plutôt d'évolution prudente et intelligente.

Certes Java ne fait pas la course aux fonctionnalités, mais elles ont le mérite d'être réfléchies et pesées.

Je trouve par exemple la notion d'interface fonctionnelle particulièrement ingénieuse, car non seulement cela permet de ne pas s'embêter à manipuler un nouveau type de données (des "fonctions-types" ou des types delegate), mais cela permet également une retro-compatibilité avec de nombreuses APIs existantes, et cela sans le moindre effort !

Les expressions Lambdas (et les références de méthode) permettront également de concevoir de nouveaux types d'APIs, mais n'oublions pas non plus les méthodes par défaut, qui permettront une meilleure évolution des APIs existantes et futures (et d'ailleurs l'API de Collections en profite bien).

Je pense vraiment qu'il s'agit d'une version qui marquera sans doute un tournant aussi important que J2SE 5.0...

Thierry Leriche-Dessirier (thierryler)

Selon moi, les trois points les plus importants de cette nouvelle version sont 1) les Lambdas 2) les streams et 3) la disparition du permgen.

L'ajout des interfaces fonctionnelles et des méthodes par défaut a été imposé par les Lambdas. Ça reste très intéressant malgré tout. À mon sens, c'est tout de même au second plan.

Les Lambdas sont synonymes de programmation fonctionnelle. Si ce n'était qu'une question de syntaxe, ça aurait eu moins d'impact. Des frameworks tels que Guava le proposent déjà, comme je le montre dans l'article intitulé "Tutoriel d'introduction à la programmation fonctionnelle avec Guava".

Avec Java 8, on va bien plus loin. On a aussi accès au "reduce" du "filter-map-reduce" mais, et surtout, cela travaille pour de vrai dans un contexte multithread/multicoeur. On pouvait déjà travailler en multicoeur en Java, surtout depuis Java 7 qui a bien défriché le terrain pour Java 8, mais il fallait se coltiner les points techniques à chaque fois, avec les risques que cela comporte et avec une syntaxe non adaptée. Java 8 rend cela "simple".

Il ne faut pas oublier que les fabricants de processeurs ne cherchent plus à augmenter la fréquence. C'est désormais une course aux cœurs. L'avenir est donc aux traitements parallèles et aux lambdas. Encore faudra-t-il bien comprendre les patterns de la programmation fonctionnelle, pour ne pas les utiliser de travers et à tout-va.

Le permgen, quant à lui, disparaît et ce n'est pas trop tôt. On pointe les projecteurs sur le langage en oubliant qu'il se passe des choses en coulisse et plus spécifiquement du côté de la JVM. N'oublions pas que Java, c'est un langage et une Virtual Machine. C'est très important. La VM est conçue pour optimiser les programmes (plan d’exécution) à l'utilisation. Ce n'est pas juste un lanceur de classe Main. Avec la disparition du permgen, on voit le rapprochement des plusieurs éditeurs de JVM/GC qui porte ses fruits.

Télécharger la nouvelle version de Java

Et vous ?

  • Que pensez-vous des nouveautés de Java 8 ?
  • Pensez-vous migrer prochainement vers cette nouvelle version ?
  • Pensez-vous également que les outils sont prêts à recevoir cette nouvelle version ?
  • Quelle est votre nouveauté préférée ?
  • Détaillez une nouveauté dans la discussion.


PS : un remerciement spécial à tous les membres ayant participé à l'élaboration de cette annonce : Mickael Baron, AdiGuba, bouye, Robin56, lunatix, CyaNnOrangehead, thierryler, alain.bernard, ced, Yohan Beschi de SOAT pour les tutoriels sur les nouveautés Java 8 (API Date et Time, Nashorn et le projet Lambda) et Olivier Croisier pour son article sur les nouveautés au niveau des interfaces.


Vous avez aimé cette actualité ? Alors partagez-la avec vos amis en cliquant sur les boutons ci-dessous :


 Poster une réponse

Avatar de tomlev tomlev - Rédacteur/Modérateur https://www.developpez.com
le 18/03/2014 à 21:25
Très bonne nouvelle pour Java, qui fait un énorme pas en avant avec cette nouvelle version

Citation Envoyé par Hinault Romaric  Voir le message
Les arrivées des lambdas, des méthodes par défaut, des interfaces fonctionnelles et de Stream vont modifier en profondeur le langage et donc l'écosystème Java tout entier.

Je ne peux pas m'empêcher de faire un parallèle avec la sortie de C# 3 (en 2008), qui apportait à peu près les mêmes fonctionnalités (ou des fonctionnalités équivalentes) :
  • lambdas : idem en C#
  • méthodes par défaut : méthodes d'extension en C#.
    En fait c'est un peu différent, mais le résultat est essentiellement le même ; en C# les méthodes d'extension étaient nécessaires pour faire fonctionner Linq, en Java les méthodes par défaut sont nécessaires pour faire fonctionner Stream. Les méthodes d'extension de C# permettent d'ajouter des méthodes à n'importe quel type ; les méthodes par défaut de Java sont plus restrictives (puisqu'elles doivent être déclarées dans une interface), mais elles sont virtuelles, donc les classes qui implémentent l'interface peuvent les redéfinir. Les deux approches ont donc chacune des avantages et des inconvénients...
  • interfaces fonctionnelles : délégués en C# (ils existaient en fait depuis la première version de C#)
  • Stream : Linq en C#. En fait Linq fournit aussi une autre syntaxe qui se rapproche un peu du SQL ; c'est juste du "syntactic sugar" qui est converti en appels de méthodes, mais ça rend certaines requêtes plus simples à écrire.


C# 3 a profondément transformé la façon de coder de la plupart des développeurs C#, en ajoutant au langage ces éléments issus de la programmation fonctionnelle. Je partage donc l'avis d'Hinault : Java 8 aura probablement le même effet sur le monde Java.

En tous cas si j'ai l'occasion de refaire du Java un jour, je serai bien content d'avoir ces fonctionnalités, dont j'aurais du mal à me passer aujourd'hui
Avatar de I_Pnose I_Pnose - Membre chevronné https://www.developpez.com
le 18/03/2014 à 23:00
Voila une évolution qui vaut son pesant de cacahuètes, c’est une très bonne nouvelle.

(Je fais partie de ceux qui râlent à propos de l’évolution lente de Java, cette version devrait me calmer pour quelques temps =P ).
Avatar de Gugelhupf Gugelhupf - Modérateur https://www.developpez.com
le 18/03/2014 à 23:06
Ce qui m'impressionne le plus avec Java 8, c'est le fait de voir qu'il évolue, de manière lente certes, mais de façon efficace au point de rester backward compatible. Aussi ce n'est pas une techno qui ajoute pour ajouter.

Je trouve les lambdas agréables, même si je préfère les méthodes par référence.
Les méthodes par défaut et static pour les interfaces peuvent avoir leurs avantages, pour ma part je n'ai pas eu de gros souci de conception à ce niveau.
Les streams (ou LINQ de C#) ne sont selon moi pas aussi puissant que le SQL.

Ce qui me rebute par contre, c'est de voir toutes ces interfaces qui alourdissent la plateforme alors qu'ils n'ont pas forcément un grand intérêt. Par exemple au niveau du package function, l'interface Function<T, R> peut être utile sachant que les variadic template n'existent pas, mais pourquoi ajouter IntFunction<R>, LongFunction<R>, DoubleFunction<R> ?...

PS: Je pense qu'il faudra attendre au moins 7-8 ans pour que les entreprises et l'éducation adoptent cette version (petit troll de la soirée ).
Avatar de tomlev tomlev - Rédacteur/Modérateur https://www.developpez.com
le 18/03/2014 à 23:15
Citation Envoyé par Gugelhupf  Voir le message
Les streams (ou LINQ de C#) ne sont selon moi pas aussi puissant que le SQL.

Bah oui mais ça fait pas la même chose, donc c'est pas vraiment comparable... SQL permet de requêter sur des bases de données, alors que les Streams permettent de requêter sur des données en mémoire (du moins dans le cas d'utilisation typique ; rien n'empêcherait a priori de faire un stream sur des fichiers par exemple)
Avatar de adiGuba adiGuba - Expert éminent sénior https://www.developpez.com
le 19/03/2014 à 10:17
Citation Envoyé par tomlev  Voir le message
[*]méthodes par défaut : méthodes d'extension en C#.

Je suis globalement d'accord concernant les apports de la programmation fonctionnelle.

Pas contre je ne le suis pas du tout concernant les méthodes par défaut : il ne s'agit pas vraiment de l'équivalent des méthodes d'extensions de C#.
Ce n'est pas un peu différent, c'est très différent malgré une syntaxe relativement proche.

Pour moi si on devrait citer un équivalent en Java des méthodes d'extensions de C#, je nommerais plutôt l'import static de Java 5.0.
Même si la syntaxe est différente, le résultat est le même : du sucre syntaxique permettant l'appel d'une méthode static.
La seule différence étant que la syntaxe des méthodes d'extensions de C# est syntaxiquement identique à l'appel d'une méthode d'instance (même si ce n'est pas le cas).
En C# si a.method() est une méthode d'extension, la résolution de la méthode se fera uniquement à la compilation, selon les namespaces utilisé.
En Java si method(a) utilise un import static, la résolution de la méthode se fera uniquement à la compilation, selon l'import utilisé.
Bref dans les deux cas on aboutit à un appel de méthode static...

Comme tu le dis, les méthodes par défaut utilisent des méthodes virtuelles. C'est une énorme différence de mon point de vue.
Donc si a.method() est une méthode d'extension, la résolution de la méthode se fera à l'exécution selon le type réel de a, comme pour n'importe quelle méthode d'instance. La seule différence étant que son implémentation n'est pas requise, et que la JVM va alors utiliser une implémentation par défaut au lieu de générer une exception...

De plus ces méthodes appartiennent réellement à l'objet, ce n'est pas juste "pour faire beau" (on peut les retrouver via l'API de reflection par exemple...)

Citation Envoyé par Gugelhupf  Voir le message
Les méthodes par défaut et static pour les interfaces peuvent avoir leurs avantages, pour ma part je n'ai pas eu de gros souci de conception à ce niveau.
Les streams (ou LINQ de C#) ne sont selon moi pas aussi puissant que le SQL.

Les méthodes par défaut ne pas d'une grosse utilité pour les développeurs d'applications, puisqu'il reste malgré tout possible de faire évoluer les interfaces en adaptant son code.
C'est par contre nettement plus utile pour les concepteurs d'API, qui ne pouvaient pas modifier leurs interfaces sous peine de "casser" le code de tous les utilisateurs de ces APIs...

Quand aux méthodes static dans les interfaces, c'est juste un petit plus en effet.
A noter toutefois qu'ils corrigent certains "défauts" des méthodes static dans une classe (elle ne "s’héritent" pas, et on doit obligatoirement utiliser la bonne syntaxe NomInderface.method() et non pas nomInstance.method()).

Citation Envoyé par Gugelhupf  Voir le message
Ce qui me rebute par contre, c'est de voir toutes ces interfaces qui alourdissent la plateforme alors qu'ils n'ont pas forcément un grand intérêt. Par exemple au niveau du package function, l'interface Function<T, R> peut être utile sachant que les variadic template n'existent pas, mais pourquoi ajouter IntFunction<R>, LongFunction<R>, DoubleFunction<R> ?...

C'est vrai ! Sur les 43 interfaces fonctionnelles, il n'y a que 9 concepts différents. Toutes les autres sont des versions intégrants les types primitifs d'une manière ou d'une autre.
Mais c'est malheureusement un problème lié aux types primitifs...

Mais je pense que les plus grosses limitations viendront des "checked-exceptions", qui ne se marient pas très bien avec les expressions lambdas.
Ou plus précisément pour utiliser une checked-exceptions, il faut impérativement que le type de l'exception soit déclarée dans la signature de la méthode de l'interface fonctionnelle

a++
Avatar de tomlev tomlev - Rédacteur/Modérateur https://www.developpez.com
le 19/03/2014 à 10:45
Citation Envoyé par adiGuba  Voir le message
Pas contre je ne le suis pas du tout concernant les méthodes par défaut : il ne s'agit pas vraiment de l'équivalent des méthodes d'extensions de C#.
Ce n'est pas un peu différent, c'est très différent malgré une syntaxe relativement proche.

Je sais bien, et j'ai d'ailleurs mentionné les différences (sans trop approfondir) dans mon message
C'est vrai que ça n'a pas grand chose à voir techniquement, mais c'est conceptuellement similaire, dans la mesure où les deux fonctionnalités permettent d'ajouter des comportements communs à des interfaces sans avoir à toucher aux implémentations existantes.

Par exemple :
  • en C#, on peut utiliser Linq sur n'importe quelle classe qui implémente l'interface IEnumerable<T> (équivalent de Iterable<T> en Java). Mais on n'allait pas demander à toutes ces classes d'implémenter les méthodes Where (filter), Select (map), Aggregate (reduce), etc. Les méthodes d'extension ont justement été introduites pour ça : ce sont des méthodes statiques qui peuvent être appelées comme des méthodes d'instance, moyennant un peu de "syntactic sugar". Du coup, toutes les implémentations de IEnumerable<T> profitent immédiatement des méthodes d'extension définies dans la classe Enumerable.
  • en Java, l'interface Collection<T> définit une defender method stream, et toutes les classes qui implémentent Collection<T> en profitent sans avoir à changer une ligne de code.
Avatar de spyserver spyserver - Membre averti https://www.developpez.com
le 19/03/2014 à 11:24
C'est marrant, tout d'un coup Java suscite l'intérêt des devs C# et C++ au final Java comble le trou sur pas mal de sujet comme la prog. fonctionnelle mais pas que, tout en restant multi-env, Java 8 marque clairement des points.

Quant au remplacement du permgen par le metaspace cela semble resoudre les pb de tuning de JVM et ainsi d'oublier les permgen space error.
Avatar de adiGuba adiGuba - Expert éminent sénior https://www.developpez.com
le 19/03/2014 à 11:48
Citation Envoyé par tomlev  Voir le message
en Java, l'interface Collection<T> définit une defender method stream, et toutes les classes qui implémentent Collection<T> en profitent sans avoir à changer une ligne de code.

Oui... mais la différence c'est que chaque implémentation sera quand même libre de proposer une implémentation qui lui est plus spécifique, ce qui n'est pas possible avec les extension method...

Et c'est bien le cas d'ailleurs dans l'API standard.
Non pas directement via la méthode stream(), mais via la méthode par défaut spliterator() qui retourne un Spliterator, une sorte de super-Iterator à la base du fonctionnement des Stream.

Cette méthode spliterator() est définie par défaut dans Iterable avec une implémentation basique, mais elle est aussi redéfinie par défaut dans les interface Collection, List, Set, SortedSed pour proposer une implémentation plus spécifique prenant en compte les caractéristiques de chaque type de collection (SIZED, ORDERED, DISTINCT, ...).

Mais ce n'est pas tout, puisque chaque implémentation réelle peut également redéfinir cette méthode pour une version encore plus spécifique, voir même pour optimiser le code de l'itération interne.
C'est ce que font par exemple LinkedList, ArrayList, HashSet, TreeSet et sûrement d'autre...

Une méthode par défaut c'est bien. Une implémentation spécifique c'est mieux

a++
Avatar de Traroth2 Traroth2 - Membre chevronné https://www.developpez.com
le 19/03/2014 à 12:09
Ah, les affaires reprennent !

Sinon, on pourrait en savoir plus sur la disparition du PermGen space ? Où est stocké le bytecode, désormais ?
Avatar de Washmid Washmid - Membre averti https://www.developpez.com
le 19/03/2014 à 13:03
Les implémentations par défaut répondent aussi à quelques problématiques auxquelles ne répondent pas les méthodes d'extension de C# :

- Déjà si ça peut éviter les IEditorPart, IEditorPart2, , IEditorPart3, IEditorPart4 etc. qu'on voit par exemple dans Eclipse au fil des évolutions... c'est un très bon point

- On a souvent besoin de fournir une implémentation par défaut pour des interface destinée à faciliter les implémentations ou instanciations de l'extérieur. Exemple : JDOM avec SAXBuilder qui implémente SAXEngine.
Ces couples interface / implémentation par défaut ont tendance à pousser les utilisateurs d'API à référencer à tord l'implémentation par défaut au lieu de de l'interface, ce qui introduit une dépendance forte souvent inutile et pouvant causer des problèmes par la suite.

Bref, conceptuellement c'est super, contrairement aux méthodes d'extensions qui sont conceptuellement un peu foireuses (comprenez par là statique = foireux, je suis un militant anti statique :p )
Offres d'emploi IT
Développeur .Net
SATELLIT - Belgique - Bruxelles
Développeur Angular JS
EKXEL IT Services & Financial Engineering - Belgique - Bruxelles
Solutec - Rhône Alpes - Lyon (69000)

Voir plus d'offres Voir la carte des offres IT
Contacter le responsable de la rubrique Accueil