Les bonnes et les mauvaises critiques sur Java — Épisode 2 : La syntaxe

La syntaxe est, à mes yeux, le plus gros problème de Java. Le langage n’a pas su évoluer avec le temps au contraire d’autres comme Python. Je sais que je cite souvent les mêmes exemples mais ils sont à mes yeux révélateurs du problème. La syntaxe est lourde à pleurer et il est hallucinant que certains concepts n’ont toujours pas été ajoutés avec le temps à la syntaxe. En voici quelque-uns :

Les chaînes templates et multilignes.

Manipuler des chaînes de caractères est une vraie plaie en Java. Il est impossible de déclarer des chaînes multilignes facilement. Le moindre retour à la ligne oblige à ajouter le caractère spécial \n  et la moindre insertion de variable oblige à casser la chaîne et la concaténer en utilisant l’opérateur + . En Ruby, en Python et en Groovy, ce type de chaîne se déclare avec trois guillemets (en Groovy) :

mais la même chose s’écrira comme ça en Java :

C’est atrocement laid et le problème c’est que des opérations de ce type, on en fait constamment en informatique. Ne serait-ce que pour tracer des logs. Ce qui fait qu’une syntaxe spécifique a dû être créée dans certaines bibliothèques. Comme SLF4J pour la gestion des logs, par exemple :

Et tout en Java est comme ça : le langage est tellement troué qu’on ne peut pas développer un projet décent en entreprise sans faire appel à une biblio courante. La gestion des dates, par exemple, est tellement misérable qu’une biblio a été développée rien que pour ça. Elle est même devenue le standard de facto en Java 7 et a été intégrée à Java 8 !

Les dictionnaires, listes, tableaux et autres itérables

C’est, à mes yeux l’autre monstruosité de Java. Et pourtant, c’est, avec la manipulation de chaînes, l’autre opération la plus courante de l’informatique. En fait, on ne fait même que ça. Même les chaînes de caractères sont en fait des tableaux dans tous les langages courants. Manipuler des données, c’est l’essence même de l’informatique. Et ces données sont presque toujours structurées en piles, files, listes, dictionnaires, bref, en structures séquentielles. Pourtant, leur manipulation est toujours une douleur anale en Java. Pourquoi ? Parce que ces structures se  manipulent encore comme des objets classiques, sans raccourci et sans sucre syntaxique.

Le pire du pire, c’est que les développeurs Java reconnaissent eux-même implicitement que la gestion des itérables en Java est à chier : ils conseillent d’écrire son propre code de management dès qu’on commence à en faire un usage intensif. Par exemple, ces structures utilisent la notation diamant : LinkedHashMap<String, String> dictionnaire = new LinkedHashMap<String, String>();  et ça, ça peut vite devenir lourd si vous faites des collections d’objets un peu velues. Du coup, il est conseillé d’écrire une classe utilitaire avec des méthodes statiques de construction pour ne pas à avoir à se taper la notation diamant à chaque fois : LinkedHashMap<String, String> dictionnaire = LinkedHashMaps.newLinkedHashMaps();

C’est d’ailleurs tellement un classique que Google en a même fait une biblio : Guava, qui a commencé comme une simple biblio de méthodes utilitaires pour la gestion des listes en Java. Voici par exemple le code de la méthode qui permet de créer une nouvelle liste dans Guava :

Voilà… Rien de plus… Juste un putain de raccourci. Vous noterez que la fonction est variadique pour pouvoir initialiser la liste à la création. Sans une méthode comme ça, vous seriez obligés d’initialiser comme ça :

Oui, il n’y a que les tableaux que l’on peut déclarer inline… En 2015…

La même chose en Groovy, ça donne ça :

Et ça, c’est encore sans vous parler de traiter ces listes. Et pour le traitement des listes, il y a deux opérations courantes en informatique : map et filter. La première, consiste à parcourir une liste pour lui appliquer une opération. La seconde, permet de réduire la liste selon un critère. Par exemple, si dans la liste du dessus vous souhaitiez construire la sous-liste des éléments qui commencent par un a, en Java, cela s’écrit comme ça :

La même chose en Groovy s’écrit comme ça :

Même chose pour appliquer une opération à tous les éléments d’une liste. Par exemple, vous souhaitez mettre une liste de chaînes en majuscules :

En Groovy :

Oui ! Il y a un putain d’opérateur pour distribuer l’appel d’un méthode à tous les éléments d’une collection ! Et c’est loin d’être la seule chose que Groovy sache faire avec les listes.

Par exemple, c’est tout con, mais Groovy n’a même plus besoin d’une seule boucle  for  :

peut s’écrire :

L’opérateur ..  sert à générer des séquences. C’est tellement un classique qu’il existe  en Bash ! Ça peut paraître futile, mais des boucles, y’en a toutes les 10 lignes de code. Mais non, Java n’a toujours pas ça en 2015…

Pas de surcharge d’opérateurs

En fait, ce point va un peu avec le précédent, parce que les collections, c’est typiquement le domaine dans lequel on souhaiterait avoir des opérateurs. En Java, la classe String  est la seule classe qui bénéficie de l’accès à l’opérateur +. C’est l’austérité la plus complète. Pourtant, avoir des opérateurs pour faire des fusions ou des fissions de listes serait tellement pratique. Encore une fois, Java se perd ici en verbosité :

Alors qu’en Groovy cela s’écrit en une ligne :

De même qu’ajouter un élément à une liste serait tellement plus facile à écrire en utilisant l’opérateur <<  plutôt que la méthode .add(). Mais ça, c’est sans vous parler des représentations des réels.

Par exemple, en Java, il existe la classe BigDecimal, qui est une représentation exacte des chiffres à virgule destinée à faire des calculs sans introduire d’erreurs de précision comme ça serait le cas avec un float . Au travail, je bosse énormément avec ça. C’est une représentation de nombre et on s’attendrait à ce que les opérateurs mathématiques soient accessibles pour cette classe. Mais non, il faudra utiliser les verbeux  .add(), .multiply(), .substract(), .divide() . Notez que les développeurs n’ont même pas pris le soin de réduire le nom de leurs méthodes à trois petites lettres : .add(), .mul(), .sub(), .div()… C’est vrai, la lisibilité du code, qu’est-ce qu’on s’en branle !?

Ça paraît con, dit comme ça, mais pourtant, voici comment calculer la moyenne d’une liste de BigDecimal en Java :

En Groovy, ça s’écrit :

Ce qui est tout de même plus intuitif et facile à lire. En fait, ça peut même être plus simple :

Sémantiquement, c’est même la définition exacte d’une moyenne : la somme des éléments d’une liste divisée par le nombre d’éléments… En pratique, la méthode sum()  utilise l’opérateur + pour calculer la somme des éléments de la liste. C’est possible parce que tout objet en Groovy, possède un opérateur +.

Pas getters et de setters natifs

Probablement le défaut le plus étonnant de Java étant donné que sa communauté a été parmis les premières à promouvoir l’utilisation intensive des accesseurs pour les variables. C’est tellement un standard que les accesseurs sont compris nativement par un grand nombre de framework et de bibliothèques Java. Par exemple, le langage de templating Thymeleaf comprend nativement la notation. Écrire "${user.name}" dans un template sera automatiquement résolu comme un appel à la méthode getName() de l’objet user. Pourtant, Java n’intègre toujours pas ce raccourci…

En Groovy, déclarer un accesseur sur une variable permet de le résoudre à l’extérieur de l’objet comme la variable elle-même. Par exemple si je déclare un objet :

Je pourrais utiliser la variable à l’extérieur de l’objet comme si elle était publique car les accesseurs sont résolus comme des raccourcis vers la variable elle-même :

Mais pas en Java…

J’avoue que de ce point de vue, je préfère la notation de C♯ qui permet de déclarer des getters et des setters en mode raccourci :

Le truc bien dans cette notation, c’est que déclarer des getters et des setters ne produit pas forcément une variable. Si vous n’utilisez pas la notation courte, aucune variable supplémentaire n’est créée. Ça n’est qu’une notation pour déclarer des méthodes. Mais ça permet de dissimuler des méthodes derrière des variables, ce qui est pratique pour présenter une même variable de plusieurs manières différentes. Par exemple un ratio, qui sera présenté comme un ratio avec son propre getter, mais sera présenté comme un pourcentage avec un autre getter sans déclarer de variable supplémentaire.

Des classes, toujours plus de classes

La bibliothèque de Java est très complète, c’est vrai et quand je suis passé du C au Java, ça m’a changé la vie. Mais le problème, c’est probablement qu’elle est trop complète. Elle est en fait bourrée de classes intermédiaires qui n’ont aucune utilité pratique dans la majorité des cas. Les ingénieurs d’Oracle semblent en fait positivement incapables de fournir des abstractions simples à utiliser pour leurs concepts. Exemple pour lire un fichier en Java 7 (ça a changé en Java 8) :

Pourquoi ai-je besoin de déclarer moi-même un FileInputStream , un BufferedReader et un InputStreamReader !?

Non seulement ça, mais en plus, il existe une seconde solution :

On peut utiliser un FileReader à la place du InputStreamReader. Mais pourquoi, alors, ai-je les deux !?

Les développeurs de Java sont les Jésus-Christ de l’informatique : ils pratiquent la multiplication des classes avec assiduité. Vous pourrez par exemple retrouver à côté de la classe File , la classe Files , une collection de méthodes statiques utilitaires pour les fichiers, la classe Arrays , à côté de la classe Array , la classe Collections  à côté de l’interface Collection , la classe Date  et la classe Calendar  (qui fait la même chose…), etc.

Simplifier la bibliothèque standard semble vraiment être mission impossible. Par exemple, pour formater une chaîne de caractères en Python, on fait comme ça :

C’est propre et simple, %  est un opérateur de la classe str. Par contre, en Java, vous ne pourrez pas faire :

Nan, il vous faudra passer par une méthode statique de la classe String :

Pour quelle foutue raison !?

 

À suivre…

Déjà 4 avis pertinents dans Les bonnes et les mauvaises critiques sur Java — Épisode 2 : La syntaxe

Attention, il est tout à fait possible d’insérer des variables dans une chaine de caractères en java sans avoir à utiliser l’opérateur de concaténation.
On peut ainsi remplacer en java l’exemple que tu cites par :
String val = « lol »;
String.format(« Une chaînen multilignen avec une variable de valeur %s », val);
Ce qui donne, si on ajoute un System.out.print() pour afficher le résultat dans la console :
Une chaîne
multiligne
avec une variable de valeur lol
      Ah oui effectivement ^^
      C’est con de pas en parler dès le début, sur le coup, j’ai cru que tu ne connaissais pas cette particularité du langage :)
      Mes excuses !
        J’en parle pas au début parce que, de manière générale, ça n’apporte rien ni à la sémantique ni à la lisibilité du code, ce qui est le reproche que je fais dans cette partie.

La discussion est malheureusement close.