[Lang] [Java] Critique
Why Java is not my favourite language
Note : ce message n'est pas neutre. Vous y trouverez peut-être même un peu de mauvaise foi. Pour écrire cet article, j'ai fait plusieurs recherches. Cependant, il peut contenir des erreurs (merci de me les signaler dans ce cas) puisque j'utilise assez rarement Java.
Java est un langage qui a directement été influencé par C++. Alors que C++ conservé la syntaxe du C pour garder une forte compatibilité (et réutiliser le code), Java a choisi de garder les défauts de cette syntaxe, sans toutefois bénéficier de cette compatibilité. C'est dommage, ils auraient pu partir sur de meilleures bases. Outre l'orienté objet, je considère que les deux principaux apports du C++ sont le système de templates et la surcharge d'opérateurs. Java a bien pensé à hériter des défauts (en particulier l'aspect purement impératif du langage), mais a "oublié" de reprendre ces deux points forts.
L'intérêt de la surcharge d'opérateurs est pourtant flagrant. Essayez d'écrire des expressions arithmétiques sans utiliser d'opérateurs (supposons par exemple que l'on manipule des vecteurs ou des matrices)... il est clair que la lisibilité du code en prend un coup. Donc, du code plus dur à lire, du code plus long, du code moins maintenable... Bien sûr, certains pourront prétendre que l'abus d'opérateurs peut nuire à la clarté, que l'on peut utiliser les opérateurs à tort et travers. Mais de la même façon, on peut très bien donner un nom incohérent à une fonction : c'est au développeur de veiller à la cohérence et à la clarté de son code. Et la surcharge d'opérateurs l'aide dans cette tâche.
L'intérêt des templates est encore plus marquant. Il suffit de regarder la STL pour s'en rendre compte. Mais en plus de la généricité, c'est un mécanisme extrêmement puissant, permettant notamment la méta-programmation. En Java, l'intégration des generics s'est faite très tardivement. Ça ressemble plus à un hack dans le compilateur qu'à une véritable fonctionnalité.
« Generics in Java are implemented by using type erasure. This meansthat information about the generic types is ignored at runtime, andonly obtainable via reflection on the static class objects. Java's approach requires additional run time type checks, does not guaranteethat generic contract will be followed, and lacks reflection on thegeneric types. Java does not allow to specialize generic classes with primitive types. In Java, it is only partially checked at compile timeand needs to be enforced using cast operations at run time, as the Java VM is not aware of generic types. » (Wikipedia)
Par ailleurs, la syntaxe de Java est extrêmement lourde. Typiquement,un code Java sera facilement 5 fois plus long (et ce n'est vraiment pas une exagération) que son équivalent dans un langage plus expressif (Ruby, Python, Caml...). C'est une perte colossale de temps, autant pour l'écriture que pour la lecture. Java est un langage très peu expressif, qui incite le programmeur à faire mal les choses, sous prétexte que c'est plus simple. Je me rappelle, en C, avoir préféré plus d'une fois utiliser un tableau statique plutôt qu'une structure de données plus adaptée, juste parce que c'est plus facile à coder. On retrouve précisément le même problème en Java. Demandez par exemple à n'importe quel programmeur Java de convertir ces 3 lignes de Caml dans son langage :
type 'a tree =
| Node of ('a -> 'a -> 'a) * 'a tree * 'a tree
| Leaf of 'a
En français : c'est un arbre binaire, dont les nœuds sont des fonctions à deux arguments et les feuilles des valeurs simples.
Il est probable que la structure obtenue ne sera soit pas aussi générique, soit pas aussi sûre, et probablement les deux. Pour faire correctement les choses, il faut facilement une trentaine de lignes de code (il faut utiliser les generics, déclarer plusieurs classes...). Dans ces conditions, on comprend le programmeur qui sacrifiera la sûreté ou la généricité.
Java est donc un langage qui cumule les problèmes des langages bas-niveau (code long pour un résultat minime, expressivité très faible), sans en posséder le moindre avantage (vitesse, accès direct à la mémoire...).
Regardons de plus près les objectifs de Java (d'après Wikipédia) :
- Utiliser une méthodologie orientée objet
Oui, mais tous les langages récents supportent aussi l'orienté objet. Mais d'une part, Java ne supporte que ce paradigme (pas de procédural,pas de fonctionnel...). Or, cela fait longtemps que l'on sait que chaque problème peut être résolu par plusieurs approches, et ce n'est pas toujours la même qui marche le mieux. Il faut donc pouvoir s'adapter au problème et utiliser la méthode la plusadaptée. Se restreindre à un seul paradigme est très mauvais.
D'autre part, cette approche orientée objet est loin d'être parfaite. Elle est assez limitée par rapport à ce que l'on voit dans Ruby par exemple : ce n'est pas du tout objet (les primitives du langage n'en sont pas). Il n'y a pas non plus d'héritage multiple (et non, les interfaces ne peuvent pas remplacer ça). Enfin, il n'est pas possible d'écrire une fonction qui accepte "tout objet possédant une méthode foo()". En C++, on peut le faire grâce aux templates ; en Haskell ou Caml, on l'a naturellement grâce aux bienfaits du typage structurel ; en Ruby ou Python, on l'a aussi avec le duck-typing. En Java, on peut théoriquement le faire à l'aide d'interfaces, mais c'est excessivement lourd : il faut déclarer une interface et modifier toutes les classes... Il faut faire ça à chaque fois que l'on désire ce type de comportement, et pour chaque méthode. En pratique, on refusera donc souvent de le faire.
- Permettre à un même programme d'être exécuté sur plusieurs systèmes d'exploitation différents
Oui, comme tous les langages récents... On a ça pour tous les langages interprétés, aussi pour Caml, aussi pour les langages .Net. Bref, rien de nouveau.
- Pouvoir utiliser de manière native les réseaux informatiques
Je ne sais pas à quoi ça correspond concrètement. À vrai dire, je n'ai jamais vu de langage qui ne permettait pas d'utiliser le réseau (avec des bibliothèques certes, mais quelle différence ?).
- Pouvoir exécuter du code distant de manière sûre
Pour faire des applets... soit. Personnellement, c'est quelque chose que je n'aime pas vraiment.
- Être facile à utiliser et posséder les points forts des langages de programmation orientés objet comme le C++.
Facile à utiliser ? Ce n'est absolument pas le cas. On est excessivement loin du principe de moindre surprise de Ruby. On est excessivement loin de la clarté d'un code Python ou de l'expressivité de Caml. Non, vraiment, on a vu mieux au niveau de la simplicité.
Et posséder les points forts du C++ ? Pour moi, c'est tout le contraire. Les points forts de C++ sont, à mon avis : ses templates, sa surcharge d'opérateurs et l'accès au bas-niveau. Java ne possède rien de tout cela.
Autres limitations et problèmes :
En essayant de faire un langage plus sûr, les concepteurs ont enlevé les pointeurs. Ça semble normal, cependant il faut penser aux alternatives. Notamment, comment écrire en Java une fonction swap(x,y) qui échange les valeurs de x et y ? En C++, on passe dans ce cas par des pointeurs...
La manipulation de fonctions est particulièrement désastreuse. Si on veut passer en argument une fonction, il faut passer par une interface. Là où en Caml on écrit 3 caractères : "(+)", en Java il faut écrire :
Interface Function {
A f(A v1, A v2);
}
Function maFonction = new Function() {
public Integer f(Integer v1, Integer v2) {
return v1 + v2;
}
}
Et que ce passe-t-il lorsque l'on veut manipuler des fonctions anonymes ou générer des fonctions à la volée ?
Le switch est lui aussi très limité : il ne fonctionne que sur des types simples (int, char, boolean...). Alors que pouvoir faire des alternatives sur des chaines de caractères, des structures, voire des arbres serait tellement agréable ! Le switch impose aussi d'utiliser un break, sous peine d'avoir des résultats (presque) aléatoires. Quid de l'élégance et de la simplicité ? Le seul argument en faveur du break que je vois, c'est de reprendre les défauts du C (par pitié, ne prétendez pas que c'est pour pouvoir regrouper plusieurs case, il est si simple de trouver une syntaxe plus élégante).
Un objet peut être "null". Ce qui renvoie une exception (null pointerexception) quand on y accède. Où est passée cette prétendue sûreté du langage ? C'est si dur que ça de ne pas introduire la valeur "null" ? C'est si dur que ça d'implémenter un type distinct, pour les quelques cas où l'on souhaite effectivement cette valeur ? Et comble absolu, même un Integer peut être null ! Ce qui permet de gagner une "Null Pointer Exception" à l'exécution. Et dire que tout langage décent permet de détecter ça à la compilation (avec tous les avantages que ça implique).
Java, un langage qu'il vaut mieux ne pas utiliser.
- LLB
- 02:33
- > Lien permanent
- > Commentaires
- > Abus ?


![[Jeu] Ideo](images_/carre1.gif)
![[Lang] [Java] Critique](images_/carre3.gif)
