Il y a 11 ans -

Temps de lecture 7 minutes

Devoxx – The evolution of Java – Past, Present and Future

Pour cette avant-dernière session de la conférence, l’incontournable Joshua Bloch remplit comme à son habitude la grande salle de Devoxx. Il nous présente aujourd’hui une rétrospective des meilleures et des pires fonctionnalités ajoutées au fil des versions de Java. Sa critique est objective et sans détours, d’autant qu’il a participé de près ou de loin à bon nombre des sujets abordés.

Java 1.1

  • Les classes internes (nested classes) : pas mal, mais auraient pu être bien mieux. Elles auraient sans doute du être statiques par défaut, et avec une syntaxe plus concise. Par ailleurs, elles ne sont pas réellement supportées par la machine virtuelle (si vous aimez compiler avec tous les warnings activés, le terme synthetic accessor method vous est sans doute familier).
  • La sérialisation : la pire fonctionnalité jamais ajoutée au langage. Joshua ne mâche pas ses mots, on se souvient que la sérialisation fait l’objet d’un chapitre complet dans Effective Java. Elle ouvre de nombreuses failles dans la correction, la sécurité et parfois la performance des programmes.

Java 1.2

  • Le modifieur strictfp : une petite « verrue » qui aurait pu être évitée. À l’origine, il garantissait la consistance des calculs flottants entre toutes les plateformes. En pratique, il est obsolète sur la plupart des architectures modernes, qui renvoient le même résultat avec et sans ce mot-clé. Dans toute sa carrière, Joshua n’a rencontré qu’une paire de cas où il avait une importance.

Java 1.4

  • Les assertions : gagnant, mais de peu. Joshua est un grand amateur d’assertions (il nous révèle que le compilateur Java contient tellement d’assertions qu’il s’exécute 4 fois moins vite quand elles sont activées). Il a tenté de prêcher la bonne parole, mais a peu été entendu. En résumé, certaines personnes utilisent et adorent les assertions, le reste les ignore.

Java 5

  • Les types génériques : une évolution incontournable, mais mal pensée. Sans aucun doute la fonctionnalité la plus controversée de Java. Elle a indéniablement renforcé le typage et favorisé l’expressivité, mais les wildcards, ajoutés à la dernière minute, ont été un désastre. Introduisant en plus des incompatibilités avec les tableaux et des messages d’erreur cryptiques, les types génériques ont désorienté les développeurs les plus aguerris. Joshua nous rappelle quelques commentaires désabusés de l’époque, qui font sourire avec le recul :

I am completely and totally humbled. Laid low. I realize now that I am simply not smart at all. I made the mistake of thinking that I could understand generics. I simply cannot. I just can’t. This is really depressing. It is the first time that I’ve ever not been able to understand something related to computers, in any domain, anywhere, period. — A Java Programmer, 2006

  • Les annotations : un succès, anecdotique pour beaucoup de cas d’usages, mais vital pour d’autres. Joshua se réjouit de pouvoir implémenter en trois diapositives de code un framework de test rudimentaire, basé sur des annotations @Test comme TestNG et JUnit.
  • Les enums : un franc succès. Elles ont révélé très peu de défauts en pratique. L’absence d’héritage est un choix délibéré, il aurait conduit à des incohérences (par exemple, le fait que la méthode values() de l’enum parente ne renvoie pas les constantes de l’enum fille).
  • La boucle for-each : une des évolutions les plus réussies à ce jour. Elle a simplifié les parcours de boucle et éliminé toute une classe d’erreurs off-by-one.
  • L’autoboxing : il aurait mieux valu étendre la généricité aux types primitifs. Mal utilisée, cette fonctionnalité peut entraîner des problèmes de performances (dans les boucles for par exemple) mais également des bugs assez tordus. Les amateurs de puzzlers apprécieront l’exemple suivant (solution en bas de l’article) :
    public class Searching {
        public static void main(String[] args) {
            String[] strings = { "1", "2", "4", "8", "16", "32", "64", "128" };
    
            // Translate String array into List of Integer
            List<Integer> integers = new ArrayList<Integer>();
            for (String s : strings)
                integers.add(Integer.valueOf(s));
    
            System.out.println(Collections.binarySearch(integers, 32, cmp));  // OK
            System.out.println(Collections.binarySearch(integers, 128, cmp)); // KO
        }
    
        static Comparator<Integer> cmp = new Comparator<Integer>() {
            public int compare(Integer i, Integer j) {
                return i < j ? -1 : (i == j ? 0 : 1);
            }
        };
    }
    
  • Les varargs : un succès mitigé, entaché par les interférences avec les génériques. Ils sont très utiles pour quelques fonctionnalités comme printf. Mais le pouvoir de nuisance des warnings de compilation « A generic array of Xxx is created for a varargs parameter » a été sous-estimé. Ceci devrait être résolu avec les simplified varargs de Java 7.
  • Les imports statiques : un gain modeste. Ils sont utiles pour les constantes d’énumérations et les classes utilitaires. Heureusement, ils n’ont pas été sur-utilisés, comme on aurait pu le craindre initialement.

Java 7

Vu la jeunesse de cette version, il s’agit plus ici de prévisions. Les puzzlers de Java 7 ne sont pas encore connus.

  • Le switch sur les chaînes de caractères : un gain modeste en terme de concision, et vraisemblablement de performances. Son seul défaut est qu’il encouragera peut-être une sur-utilisation des chaînes de caractères, qu’il vaut mieux encapsuler dans des types personnalisés le plus tôt possible.
  • Les littéraux binaires, les underscores dans les littéraux numériques : un gain modeste. Les underscores seront aimés et beaucoup utilisés, peut-être trop ;-).
  • Le multicatch : un gain assez conséquent. Il élimine les blocs catch redondants.
  • La transmission d’exception sans perte de type (more precise rethrow) : un gain modeste. La fonctionnalité fait ce que l’on attend d’elle, reste à voir sa valeur ajoutée à l’usage. Le problème subsistera quand on veut stocker l’exception et la relancer dans un autre thread.
  • L’opérateur diamond : un gain important. Complexe en interne, mais transparent pour les développeurs, il simplifie l’écriture du code, et supprime surtout du bruit inutile à la lecture.
  • Le try-with-resources : un gain énorme. Sans compliquer le language, il simplifie la syntaxe et résout un problème sérieux et fréquent : Joshua nous révèle que le code du JDK 6 gérait mal les ressources dans 70% des cas !
  • Les varargs simplifiés : un gain modeste. Comme indiqué ci-dessus, il élimine un warning pénible lié à l’utilisation d’arguments variables d’un type générique. Il sera surtout utilisé par les développeurs de librairies.

Conclusion

  • La plupart des changements des premières versions étaient assez bons, mais une meilleure conception des classes internes aurait sans doute limité le besoin d’intégrer les lambdas dans Java 8. La sérialisation a fait plus de mal que de bien.
  • Le bilan de Java 5 est mitigé. En particulier, les erreurs de conception des types génériques ont dilapidé le « budget de complexité » du langage, ce qui lui a fait beaucoup de mal sur le long terme. « L’esprit de Java », insufflé initialement par James Gosling, s’est détérioré au fil des versions.
  • Les changements dans Java 7 sont plus prometteurs. Apparemment, la leçon de Java 5 a été retenue, les évolutions ont été introduites avec plus de prudence.
  • Joshua est plus inquiet à propos de Java 8 : pour lui, le projet Lambda actuel a réussi à tempérer les excès de ses premières propositions, mais reste trop complexe. Les évolutions des versions suivantes lui font également peur (dans sa conférence du mercredi, Mark Reinhold a mentionné entre autres : une JVM auto-optimisée, la réification et l’unification des types primitifs, un protocole méta-objet…).

Pour finir, l’auteur de How to Design a Good API and Why it Matters nous délivre son « sermon » (selon ses propres termes) :

  • « More is not better » : avant d’ajouter une nouvelle fonctionnalité, démontrer son utilité avec de solides arguments (typiquement, une analyse statique du code existant).
  • Résister aux sirènes et aux effets de mode : « Java va mourir si nous n’ajoutons pas X », « Tous les autres langages ont Y »…
  • Rendre les changements les plus simples possibles, éviter les modifications importantes du système de types.
  • Et enfin… ne jamais ajouter une fonctionnalité au dernier moment (on repense bien sûr aux wildcards dans les génériques, que Joshua semble considérer comme sa billion-dollar mistake).

Néanmoins, Joshua termine sa présentation en nous témoignant son attachement au langage Java.


Solution du puzzler : le comparateur manipule des objets Integer. Or il utilise ==, qui se base sur les références et non les valeurs. Le cache de Integer.valueOf masque le problème pour les valeurs entre -128 et 127 ; au delà, le 128 passé en paramètre de binarySearch n’est pas le même objet que le 128 stocké dans la liste, donc le comparateur les considère comme différents et la recherche échoue.

Publié par Nicolas Jozwiak

Nicolas est delivery manager disposant de 12 ans d’expérience en conception et développement. Son parcours chez un éditeur avant son entrée chez Xebia lui a notamment permis de développer de solides compétences dans le domaine de la qualité et de l’industrialisation (tests, intégration continue, gestion de configuration, contrôle qualité). Bénéficiant d’une expérience très solide de mise en place des méthodes agiles et d’accompagnement d’équipes sur le terrain, il s’attache à mettre à profit quotidiennement son expérience qui est reconnue pour son approche pragmatique, proactive et pédagogique.

Commentaire

2 réponses pour " Devoxx – The evolution of Java – Past, Present and Future "

  1. Published by , Il y a 11 ans

    Personnellement je ne suis pas trop d’accord concernant les Generics.
    C’est sûr que les wildcards ne sont pas évident de prime abord, mais ils sont une nécessité lorsqu’on manipule des Generics. Leur syntaxe n’est peut-être pas évidente, mais je ne vois pas vraiment comment elle pourrait être amélioré…

    a++

  2. Published by , Il y a 11 ans

    J’ai la sensation que même les gourous Java sont perplexes sur l’évolution du langage malgré certaines évolutions dans le bon sens.

    « Résister aux sirènes et aux effets de mode »
    => D’un coté, j’ai la sensation que Java met tellement de temps à évoluer qu’il passe forcement au dessus des effets de mode du coup, cette règle est par nature toujours respectée.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Sapient, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.