IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Java Discussion :

Java 8 est disponible, la plate-forme se met aux expressions lambdas


Sujet :

Java

  1. #1
    Responsable .NET

    Avatar de Hinault Romaric
    Homme Profil pro
    Consultant
    Inscrit en
    Janvier 2007
    Messages
    4 570
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Cameroun

    Informations professionnelles :
    Activité : Consultant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Janvier 2007
    Messages : 4 570
    Points : 252 372
    Points
    252 372
    Billets dans le blog
    121
    Par défaut Java 8 est disponible, la plate-forme se met aux expressions lambdas
    Java 8 est disponible, la plate-forme se met aux expressions lambdas
    tour d'horizon des nouveautés

    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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
      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 - Visualiser dans une fenêtre à part
      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 - Visualiser dans une fenêtre à part
      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 - Visualiser dans une fenêtre à part
      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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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.

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 753
    Points
    39 753
    Par défaut
    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

  3. #3
    Membre chevronné
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2007
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Décembre 2007
    Messages : 677
    Points : 2 137
    Points
    2 137
    Par défaut
    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 ).

  4. #4
    Modérateur
    Avatar de Gugelhupf
    Homme Profil pro
    Analyste Programmeur
    Inscrit en
    Décembre 2011
    Messages
    1 325
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Analyste Programmeur

    Informations forums :
    Inscription : Décembre 2011
    Messages : 1 325
    Points : 3 766
    Points
    3 766
    Billets dans le blog
    12
    Par défaut
    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 ).

  5. #5
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 753
    Points
    39 753
    Par défaut
    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)

  6. #6
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    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++

  7. #7
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 753
    Points
    39 753
    Par défaut
    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.

  8. #8
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Décembre 2011
    Messages
    270
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2011
    Messages : 270
    Points : 559
    Points
    559
    Par défaut
    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.

  9. #9
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    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++

  10. #10
    Membre émérite

    Profil pro
    Inscrit en
    Décembre 2003
    Messages
    3 995
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 995
    Points : 2 522
    Points
    2 522
    Par défaut
    Ah, les affaires reprennent !

    Sinon, on pourrait en savoir plus sur la disparition du PermGen space ? Où est stocké le bytecode, désormais ?

  11. #11
    Membre averti
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    187
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 187
    Points : 433
    Points
    433
    Par défaut
    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 )

  12. #12
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 753
    Points
    39 753
    Par défaut
    Citation Envoyé par adiGuba Voir le message
    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...
    Tout à fait... et je l'avais justement souligné dans mon message initial

  13. #13
    Modérateur

    Homme Profil pro
    Développeur java, access, sql server
    Inscrit en
    Octobre 2005
    Messages
    2 713
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur java, access, sql server
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 713
    Points : 4 792
    Points
    4 792
    Par défaut Interfaces fonctionnelles
    J'ai une question toute simple : quel est l'intérêt technique de l'Interfaces fonctionnelles ?

    Dans mon travail de développement de tous les jours, j'utilise des interfaces à 1,2,3 ... n méthodes.
    Ensuite, quand je fais une classe qui implémente l'interface, mon IDE me créé immédiatement le squelette des méthodes.

    Quel est l'intérêt technique de distinguer les interfaces à 1 méthode des interfaces à plusieurs méthodes ?

  14. #14
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 753
    Points
    39 753
    Par défaut
    Citation Envoyé par Népomucène Voir le message
    J'ai une question toute simple : quel est l'intérêt technique de l'Interfaces fonctionnelles ?
    C'est pour avoir un type pour représenter les expressions lambda. Une méthode qui prend en paramètre une interface fonctionnelle peut accepter une expression lambda avec la signature correspondante.

  15. #15
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par tomlev Voir le message
    Tout à fait... et je l'avais justement souligné dans mon message initial
    Oui je sais (je l'ai d'ailleurs indiqué).
    C'est juste que je pense que cela fait vraiment une énorme différence, et que du coup ca me gène de dire que c'est un équivalent des extensions method de C#.
    C'est vraiment tout autre chose


    Citation Envoyé par Népomucène Voir le message
    J'ai une question toute simple : quel est l'intérêt technique de l'Interfaces fonctionnelles ?
    Cela sert uniquement aux expressions lambdas et références de méthode, qui ne peuvent être affectés qu'à une interface fonctionnelle.

    Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Runnable run = () -> System.out.println("Hello World");
     
    ActionListener listener = (ActionEvent e) -> { System.out.println(e) };
     
    Comparator<String> comparator = String::compareToIgnoreCase;
     
    Supplier<Thread> threadFactory = Thread::new;

    a++

  16. #16
    Modérateur
    Avatar de Gugelhupf
    Homme Profil pro
    Analyste Programmeur
    Inscrit en
    Décembre 2011
    Messages
    1 325
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Analyste Programmeur

    Informations forums :
    Inscription : Décembre 2011
    Messages : 1 325
    Points : 3 766
    Points
    3 766
    Billets dans le blog
    12
    Par défaut
    Népomucène
    Quel est l'intérêt technique de distinguer les interfaces à 1 méthode des interfaces à plusieurs méthodes ?
    Lorsque tu utilises un lambda, tu ne précises pas le nom de la méthode. Si tu ne précises pas le nom de la méthode, tu ne sais pas forcément si c'est ton "handler" qui sera appelé ou une autre méthode de la classe, sachant que plusieurs méthodes d'une classe peuvent avoir la même signature.

    Si tu veux avoir un "max" de liberté avec les lambdas, tu peux utiliser l'interface Function<T, R>.

  17. #17
    Expert éminent
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2007
    Messages
    2 161
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2007
    Messages : 2 161
    Points : 7 952
    Points
    7 952
    Par défaut
    Que pensez-vous des nouveautés de Java 8 ?
    Enfin une vraie évolution de Java comparable à la 5 !!
    Ca fait du bien et renouvelle l'intérêt pour ce langage que j'aime tant.

    Pensez-vous migrer prochainement vers cette nouvelle version ?
    Pour mes projets perso, oui assurément.
    Ca me permettra de faire mumuse et de m'approprier les nouvelles fonctionnalités et syntaxes.
    Par contre, au niveau pro, je crains que ça ne soit pas pour demain...
    En effet, il y a pas 2 mois, j'ai travaillé sur un projet en 1.4
    Et sur mes autres projets, Java 6 pointe tout juste le bout de son nez...
    Alors je ne suis pas très optimiste sur son intégration rapide dans le milieu pro mais je me rappelle avoir été surpris par la vitesse d'intégration de Java5 car elle apportait beaucoup donc peut être que Java8 aura le même sort, espérons

    Pensez-vous également que les outils sont prêts à recevoir cette nouvelle version ?
    Pas encore mais ça viendra vite

    Quelle est votre nouveauté préférée ?
    Je dois dire que la fin du permgen me fait particulièrement plaisir
    Je suis assez content par l'arrivée des Stream qui me facilitera beaucoup la vie car je travaille beaucoup en multithread et que jusqu'à présent, c'était très lourd à gérer.


    De belles nuits blanches en perspective pour tester et intégrer tout ça

  18. #18
    Membre chevronné
    Avatar de la.lune
    Homme Profil pro
    Directeur Technique
    Inscrit en
    Décembre 2010
    Messages
    545
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Comores

    Informations professionnelles :
    Activité : Directeur Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2010
    Messages : 545
    Points : 2 084
    Points
    2 084
    Par défaut
    Citation Envoyé par adiGuba Voir le message
    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.
    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, ...).
    +1
    Ça aussi étant le point le plus fort, voir qu'on peut aussi enchaîner des implémentations par défaut tant que nous sommes dans une hiérarchie d'interface et puis après le polymorphisme par excellence se joue sur des implémentation par défaut.

    Juste pour expliquer en plus ce que notre expert adiGuba a dit avec un peu plus de détail technique pour montrer réellement ce qui se passe et après montrer aussi les limites non cité ici des méthodes d’extension en C#.

    Alors regardons ici qu'on a l'interface Collection qui hérite de l'interface Iterable(java 5), mais maintenant il ajoute la méthode spliterator() et propose une implémentation par défaut de spliterator(), qui ne fait que retourner un objet de type Spliterator(Java 8),

    Pour ceux qui veulent comprendre c'est quoi Spliterator: alors un objet de type Spliterator c'est tout simplement un objet qui permet le parcourt et le partitionnement des éléments d'une source. La source des éléments appartenant à un Spliterator pourraient être, par exemple, un tableau, une collection, un canal IO, .. .

    Ainsi, l’itération sur les éléments et le partitionnement(en cas de partitionnement) dépendra des caractéristiques de la source comme signalé par aduGuba, si par exemple les élément sont ordonnés, ou la source possède une taille, ou si les éléments sont distinct deux à deux, et plus d'autres caractéristiques comme les sources à accès concurrente.

    L'API Spliterator a été conçu pour soutenir le parcours parallèle efficace, en plus du parcours séquentiel, en s'appuyant sur la décomposition ainsi qu'une itération en un élément unique. Et comme adiGuba l'a dit : c'est une sorte super-Iterator.

    A présent revenons sur notre sujet: alors, notre fameux Iterable dans l’implémentation par défaut(une seule ligne de code ) ça consiste juste à appeler une des méthodes static de la classe Spliterators(avec s) qui retour un objet de type Spliterator.

    Mais il y en a pas qu'une seule méthode, et toutes dépendent des paramètres et des caractéristiques, alors dans l'interface Iterable, on appel la méthode qui retourne l'objet le plus moche et appelant aussi la méthode la plus moche
    Code Java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    default Spliterator<T> spliterator() { 
           return Spliterators.spliteratorUnknownSize(iterator(), 0);   }
    Maintenant nous sommes dans l'interface Collection qui étend Iterable, elle décide elle aussi de fournir une implémentation par défaut de spliterator(). Cette implémentation n'a pas vraiment quelque chose de particulier que d’appeler la méthode de Spliteratorsspécifique aux Collections sur this (comme on a dit les méthodes par défaut sont des méthodes d'instance).
    Code Java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    @Override 
    default Spliterator<E> spliterator() {
            return Spliterators.spliterator(this, 0);
        }

    Alors, les interfaces List, Set, SortedSettoutes héritent de l'interface Collection, et vu leur contexte, ils définissent elles aussi chacune une implémentation par défaut de spliterator() en appelant la méthode avec les bon paramètres spécifiques.

    Alors maintenant vient notre nouveau grand joujou Stream qu'on a besoin dans l'interface Collection pour la méthode default Stream stream(). Alors alors pour cette méthode pusse ouvrir le flux sur la collection en cours et itéré efficacement il a besoin du Sliterator sur la collection encours que la méthode spliterator() nous procure.

    Alors dans l'API standard, cette méthode n'est pas aussi chargé qu'on allait le penser, toute la techno de la programmation fonctionnelle(les évaluation lazy et autre), et la parallélisation ne sont pas codé dans cette méthode.

    Déjà Stream est une interface il n a que les signatures des méthodes, sauf la méthode default Builder<T> add(T t) qui manipule en deux ligne une méthode virtuelle de l'interface.

    Ainsi, dans Collection l’implémentation par défaut sur Stream, s'est fait juste en six lignes de codes(en deux méthodes) seulement ont été ajouté à l'interface mère des Collections
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    default Stream<E> stream() {        
    return StreamSupport.stream(spliterator(), false); //false => isParallel()==false, donc séquentielle
        }
     
    default Stream<E> parallelStream() {
            return StreamSupport.stream(spliterator(), true); //true => isParallel()==true, donc un traitement parallèle
        }
    Les autres interfaces List, Set, SortedSetn'ont pas implémentés ces deux méthodes, l’implémentation par défaut de Collection suffit.

    Mais là où je veux en venir vraiment ce n'est pas juste le fait que je peux appeler la méthode stream() sur mes bon vieux implémentations des Collection qui datent depuis en faisant des truc comme ça
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    ArrayList<String> languages=new ArrayList<>(Arrays.asList("Java","C#","C++","C","JavaScript"));                                   
                        languages.stream()               
                                 .filter(s->s.startsWith("J"))
                                 .forEach(System.out::println);
    Mais la question est que lors de la compilation d'un bout de code pareil, la méthode stream() est bien évidement celle de Collection et elle est compilée avec la méthode spliterator() de Collection, et pourtant à l'exécution c'est l’implémentation spécifique de spliterator()de ArrayList qui est appelé, une méthode qui a été ré-implémenté comme itérateur optimale en fonction du type de la liste.

    ÇA C'EST TRÈS FORT pour les méthodes par défaut vraiment chapeau aux concepteur de Java.

    Et c'est ça le comportement de polymorphique qu'on ne peut jamais produire avec le langage C#, avec les méthodes d’extension comme tomlev a souligné en partie au début, par contre quand tu dis:
    Citation Envoyé par tomlev Voir le message
    Les méthodes d'extension de C# permettent d'ajouter des méthodes à n'importe quel type
    Ce que vous dites là n'est pas vrai, on ajoute rien au type, comme déjà dit on fait juste des appel static sur l'objet. Les méthodes d’extensions le contexte est purement static, pas d'encapsulation et aucun accès aux membres privés ou protégé contrairement aux méthodes par défaut de java.

    Ces méthodes d’extension j'allais dire sont comme les fonctions globales en C++, et on gère la portée avec les namespace, seulement avec cette méthode l'objet qu'on va placé à gauche suivit du « . » , et celle qui est mis en paramètre de cette fonction.

    Ainsi, leurs portée se réduisent au namespace et si on se permet de définir une autre méthode sur un autre namespace mais avec le même nom et on importe les deux namespaces c'est finit ça ne compile pas . Il n y a même pas un mécanisme de gestion de conflit, car où est ce qu'on pourrait gérer ça?Mais pour faire beau, on laisse les utilisateurs comprendre comme si c'est de POO avec de la sucre syntaxique, mais ça n'a rien n'avoir avec ce qui se passe et la structure de la classe reste intouché.

    Contrairement à Java 8 la méthode par défaut appartient vraiment à l'interface et à toute classe qui implémente l'interface, et en cas de redéfinition on peut à avoir accès aux membres privés et protégés, ça ce qu'on appel ajouter une méthode à un type.

    Java ouvre également la porte à l'héritage multiple d'implémentation, mais cela peut aussi créer des conflits mais tout est bien pensé, si j'hérite de deux interfaces différentes et que ça trouve que les deux interfaces ont des méthodes par défaut portant le même nom, alors les expert Java ont ajouté une nouveauté et mis en place de règles à des règles de gestion de conflit : l’implémentation de la classe est prioritaire de même que celle qui est spécifique. Mais si je redéfinis sans donner de code là, où j'ai de problèmes.
    Avant en Java si je fais ceci
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    interface IA{    
    public void m1();
    }
    interface IB{
        public void m1();
    }
    class A implements IA , IB{
            public void m1() {
            }
    }
    Ça va très bien et A est bien de type IA et IB
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     A a= new A();
    boolean b1= ( a instanceof IA); // b1==true
    boolean b2= ( a instanceof IB); // b2==true
    Mais si je fais
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    interface IA{    
    default void m1(){
    //implémentation par défaut
    }
    }
    interface IB{
        default void m1(){
    //implémentation par défaut
    };
    }
    class A implements IA , IB{
    }
    Là ça ne va pas du tout, il faut implémenter la méthode
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class A implements IA , IB{
    public void m1(){
    }
    }
    Là au moins ça compile.
    Mais là, je n'ai pas spécifié la méthode que je veux, comme je peux donner une implémentation spécifique mais si je veux que c'est la méthode de IA je dois faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    public class Test implements IA, IB {     
    public void m() {
            IB.super.m1();//m1() n'est pas static elle est d'instance, je ne peux pas faire IB.m1()
        }
    }
    Et pourquoi pas faire appeler les deux en cas de besoin si juste c'était un conflit de nom, mais les deux implémentations par défaut sont différentes, je peux faire comme ça:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class A implements IA , IB{
        @Override
        public void m1(){
        IB.super.m1();
        IA.super.m1();
        }
    Mais ce tu dis ici aussi que :
    les méthodes par défaut de Java sont plus restrictives (puisqu'elles doivent être déclarées dans une interface), ........ Les deux approches ont donc chacune des avantages et des inconvénients...
    Je ne vois pas l’inconvénient sur le fait d'ajouter une méthode à l'interface, ce n'est pas une classe. Où est le problème. Vous voulez qu'on ajoute une méthode à une classe en faisant de la magie.

    Déjà vous avez vu que c'est en quelques lignes de code ajoutés à l'interface Collection que tout est résoudre. Une seule méthode que je n'ai pas mentionné ici ajouté à Collection et codé par défaut utilisant des méthodes aussi virtuelles pure c'est la méthode default boolean removeIf(Predicate<? super E> filter). Et on peut aussi à tout moment faire quelque chose de static à la Java 5 si on veut.

    Java certes évolue lentement, je reconnais son retard dans les expression lamdas, ils sont venus au bon moment mais, il vient surrement en plus on voit un travail bien fait et de la vraie expertise.

    Le dernier point que j'aurais aimé souligner et tout le monde le dit ici, Java n'appartient pas à Oracle seul comme C# appartient à Microsoft, Microsoft peut se permettre de faire tout ce qu'il veut, mais en Java, il faut que la JCP en entier participe dans la décision.

    Java évolue au même rythme aussi dans tous les OS et Oracle doit faire tous le moyens possible qu'à la sortie de Java, ça soit disponible dans tous les OS, et je vous annonce que même la version finale de Java8 pour les architecture ARM est sortie. Par contre Microsoft se soucie Juste de son OS. Ainsi les planification de la sortie des versions de Java tiennent compte de toutes les version cibles.

  19. #19
    Membre habitué

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    125
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Janvier 2007
    Messages : 125
    Points : 175
    Points
    175
    Par défaut permgen & memory leaks
    Sinon, on pourrait en savoir plus sur la disparition du PermGen space ? Où est stocké le bytecode, désormais ?
    http://java.dzone.com/articles/java-8-permgen-metaspace

    Un autre zone mémoire. On peut y mettre une taille max si on veut.

    En gros, si tu as un classloader leak, ça va continuer à être problématique.

    J'ai souvent entendu croire que le perm gen space était un problème en soi. Quand on rencontre le fameux "Out of memory: perm gen space", il y a une fuite de mémoire (application, serveur d'app, ...).

    Jrockit n'a pas de permgen space, mais je constate régulièrement des class loader leaks dans des applications et sur des serveurs d'apps. Ça a juste tendance à ne pas être remarqué au début.

    Pas de magie ici : il faut continuer à corriger les bugs.

  20. #20
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par la.lune Voir le message
    Juste pour expliquer en plus ce que notre expert adiGuba a dit
    Juste pour préciser : je ne suis pas un "expert".
    C'est juste le titre donné par le forum car j'y ai beaucoup participé (à un époque), mais cela s'arrête là !


    Citation Envoyé par la.lune Voir le message
    Alors, les interfaces List, Set, SortedSettoutes héritent de l'interface Collection, et vu leur contexte, ils définissent elles aussi chacune une implémentation par défaut de spliterator() en appelant la méthode avec les bon paramètres spécifiques.
    Cela va même plus loin car chaque implémentation spécifique (ArrayList, LinkedList, TreeSet, HashSet, etc.) redéfini également la méthode spliterator() pour fournir une version encore mieux adapté...

    A titre d'exemple, sur les principales collections cela donne ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # Nom de la collection => Nom du Spliterator (caractéristiques)
                tableaux => Spliterators$ArraySpliterator (ORDERED, SIZED, SUBSIZED)
               ArrayList => ArrayList$ArrayListSpliterator (ORDERED, SIZED, SUBSIZED)
              LinkedList => LinkedList$LLSpliterator (ORDERED, SIZED, SUBSIZED)
                 TreeSet => TreeMap$KeySpliterator (ORDERED, DISTINCT, SORTED, SIZED)
                 HashSet => HashMap$KeySpliterator (DISTINCT, SIZED)
           LinkedHashSet => Spliterators$IteratorSpliterator (ORDERED, DISTINCT, SIZED, SUBSIZED)
    ConcurrentLinkedQueu => ConcurrentLinkedQueue$CLQSpliterator (ORDERED, NONNULL, CONCURRENT)
    ConcurrentSkipListSe => ConcurrentSkipListMap$KeySpliterator (ORDERED, DISTINCT, SORTED, NONNULL, CONCURRENT)
      ArrayBlockingQueue => Spliterators$IteratorSpliterator (ORDERED, NONNULL, CONCURRENT)
     LinkedBlockingQueue => LinkedBlockingQueue$LBQSpliterator (ORDERED, NONNULL, CONCURRENT)
     LinkedBlockingDeque => LinkedBlockingDeque$LBDSpliterator (ORDERED, NONNULL, CONCURRENT)

    Citation Envoyé par la.lune Voir le message
    Par contre, en prenant l'exemple des interfaces Map et StoredMap, ils n'ont pas fourni d’implémentation par défaut, la déclaration spliterator() n'existe même pas dans l'interface pour se contenter de l’implémentation par défaut existante dans l'interface Collection.
    Map n'héritent pas de Collection
    Par contre ses méthodes keySet(), values() et entrySet() retournent bien des Collections avec un Spliterator spécifique à chaque fois...

    Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
          HashMap$KeySet => HashMap$KeySpliterator (DISTINCT, SIZED)
          HashMap$Values => HashMap$ValueSpliterator (SIZED)
        HashMap$EntrySet => HashMap$EntrySpliterator (DISTINCT, SIZED)
          TreeMap$KeySet => TreeMap$KeySpliterator (ORDERED, DISTINCT, SORTED, SIZED)
          TreeMap$Values => TreeMap$ValueSpliterator (ORDERED, SIZED)
        TreeMap$EntrySet => TreeMap$EntrySpliterator (ORDERED, DISTINCT, SORTED, SIZED)


    Citation Envoyé par la.lune Voir le message
    Déjà vous avez vu que c'est en quelques lignes de code ajoutés à l'interface Collection que tout est résoudre. Une seule méthode que je n'ai pas mentionné ici ajouté à Collection et codé par défaut utilisant des méthodes aussi virtuelles pure c'est la méthode default boolean removeIf(Predicate<? super E> filter). Et on peut aussi à tout moment faire quelque chose de static à la Java 5 si on veut.
    Sauf que même dans cet exemple les méthodes par défaut on un gros avantages sur les méthodes static ou les méthodes d'extensions.

    En effet la méthode removeIf() est définie comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
        default boolean removeIf(Predicate<? super E> filter) {
            Objects.requireNonNull(filter);
            boolean removed = false;
            final Iterator<E> each = iterator();
            while (each.hasNext()) {
                if (filter.test(each.next())) {
                    each.remove();
                    removed = true;
                }
            }
            return removed;
        }
    Bref c'est une simple itération qui supprime les éléments avec remove(), selon la condition du filtre.

    C'est très bien mais cela peut avoir des effets de bord selon l'implémentation de la Collection.
    Par exemple l'opération remove() est très couteuse sur une ArrayList, car cela oblige à décaler les éléments du tableau interne. Donc le fait d'appeler plusieurs fois remove() implique que l'on va effectuer plusieurs fois ce décalage (autant de fois qu'il y a d'élément à supprimer), ce qui peut s'avérer assez couteux !

    Du coup ArrayList redéfinie cette méthode removeIf() afin de marquer les éléments à supprimer, et de n'effectuer le décalage qu'une seule et unique fois même si on supprime plusieurs éléments...


    a++

Discussions similaires

  1. Quelle API Java pour un jeu de plate forme 2D ?
    Par dawadam dans le forum API graphiques
    Réponses: 0
    Dernier message: 16/06/2011, 22h25
  2. [java] Moteur de jeu de plate-forme
    Par luckyvae dans le forum Projets
    Réponses: 12
    Dernier message: 15/08/2007, 22h06
  3. Message: 'ce symbole est propre à une plate-forme'
    Par neho88 dans le forum Delphi
    Réponses: 4
    Dernier message: 18/10/2006, 15h14

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo