blog2geek.com
LLBAvatar de LLB

38 billets | Profil

Recherche Google

ce blog tous
Derniers billets Connexion
Archives

test

16/02/2007

[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).

Le typage doit être fait explicitement, et à la main. C'est un assez gros problème, puisque on ne pense pas forcément à la généricité dès le départ. Demandez par exemple à quelqu'un d'écrire la fonction "min". Il y a des chances pour qu'il n'écrive cette fonction que pour des entiers. Quel dommage !

De plus, devoir écrire les types à chaque fois alourdit le code, et empêche au final de manipuler des types plus complexes. On a souvent en Caml des types très longs. Par exemple : le type "tout objet qui possède une variable entière x et une fonction ToString sans argument et renvoyant une chaine de caractères". Ou encore, en F# ou en Haskell, on trouve : le type "toute fonction allant de 'a dans 'b, pour tout 'a et tout 'b, à condition qu'il existe d'une part une surcharge de l'opérateur plus allant de ('b, 'b) dans 'a ; et d'autre part une surcharge de l'opérateur moins unaire allant de 'a dans 'a". En Java, ce ne serait même pas imaginable.

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).

Et puis, il y a toutes ces choses qui paraissent insignifiantes, mais qui énervent toujours un peu : le nom des fichiers est imposé (le même que la classe qu'il contient), à la casse près (personellement, j'ai toujours trouvé moches les noms de fichiers commençant par une majuscule). Et cette absence de pré-processeur, de compilation conditionnelle, cette manie d'obliger le développeur à indiquer quelles exceptions ne sont pas rattrapées, cette limitation à une classe publique par fichier, etc.


Java, un langage qu'il vaut mieux ne pas utiliser.

> Rédiger un commentaire

22:16 16/02/2007 - psyclik

Très interessant ton point de vue LLB. Mais comme tu t'en doutes, je ne suis pas tout à fait d'accord :).
 
Premier point de désaccord : Java n'est pas fait pour concurencer CAML, Python, ou même C++ ! Il est fait, à en croire leur politique, les frameworks existant etc... pour pouvoir écrire rapidement des applications d'entreprises. Il propose tous les outils pour ça. Je te met au défi de coder un PGI complet en C++ sachant s'interfacer avec plusieurs applis, dans différents langages, sur différents OS etc... Le seuls frameworks concurent, à ma connaissance, est .Net, et sur tous les points que tu cites hormis la surchage d'opérateurs, son langage "inmanquable", C#, est un clone de Java.
 
La syntaxe java est pas si lourde que ça, et des outils, tels qu'Eclipse, te font quasiment tout le boulot. Un eclipse bien configuré et tu ne te penche quasiment plus que sur l'aspect métier de ton soft. Aucun des langages que tu cite ne possède d'IDE qui rivalise (je ne sais plus si tu as cité .Net, mais même Visual se prends une pile par eclipse). Java en tantq ue tel, ce n'est rien, c'est un ensemble de technologies qui te permettent de gagner du temps.
 
En ce qui concerne les méthodes anonymes, as tu regardé du coté de Java 6 ? Tu y trouvera ton bonheur si ma méoire est bonne :). Pour le point des templates, ce que Java perds avec les generics, il le gagne sur la lisibilité du code. Un code C++ "over templated" est juste cryptique. C'est plus "type safe" ? Sans doute. C'est plus puissant ? Sans doute. Mais le temps perdu :
1. A coder.
2. Par les gens qui devront ensuite exploiter ton code.
Est juste hérétique.
 
Pour ce qui est du "pas full objet" : les types primitifs ont été ajouté à cause de raleurs qui ne voulait pas taper 4 caratères de plus pour définir un type. A l'origine, il n'existait pas, et n'aurait jamais du exister autrement qu'en péprocessing, éventuellement.
 
Du point de vue de l'execution de code distant, tu frises les records de mauvaises foie :). Les applets, c'est 2% de l'iceberg. Quand on parle de code distant, on parle plutôt de Webservices, SOAP ou RMI. Certainement pas d'applets. 
 
Et oui, Java est simple à utiliser : la syntaxe est claire. Si tu as déja codé dans n'importe quel lanage (avant Java je n'avait fait que du Smalltalk, langage que j'apprécie énormément, mais qui n'a rien à vorie avec Java), tu passe à java en 3 minutes montre en main. Si tu n'a jamais codé, tu galerera sans doute un peu sur la conception Objet, mais certainement pas sur le langage. Tu parles à ce sujet de ruby et de sa philosophie de la moindre surprise. J'aime beaucoup Ruby, mais je dois toujours coder avec de la doc à coté. Java, non. Les noms de méthodes sont plus expressifs, et la syntaxe parfois fantasque de ruby, meme si elle me plait, nui souvent à la sobriété du code, et j'ai parfois l'impression de regarder de l'ascii art, plus qu'une logique métier. Je connais ton penchant pour l'ascii art, mais là, on ne parle pas de la même chose ;). 
 
Bref, comme tu le vois, nos positions respectives n'ont pas changées :). Ceci dit, il ne faut vraiment pas voire Java uniquement par le coté syntaxique du langage. Il a des défauts j'en conviens volontiers, mais aussi ses qualités. Et _surtout_ il faut l'envisager comme un ensemble de technologies étudiées pour être exploitées en synergie : Java + struts + eclipse + hibernate. Je ne connais pas d'autres "combinaisons" qui rivalise en terme de productivité et de flexibilité.
 
Si tu connais, ca m'interesse :).
 
Bref, bien qu'en désaccord, je trouve ton  point de vue assez interessant, et pour ta peine, je risque de te linker :).

02:32 17/02/2007 - LLB

Ouais, un troll !
Soit, les langages ont des objectifs différents. Mais il faut reconnaitre que beaucoup d'utilisations se recoupent (vu que ce sont quand même des langages généralistes). Tu reconnais que .Net est un concurrent. Hé bien, sache que F# est un langage .Net basé sur Caml. On bénéficie de la puissance des deux, et le résultat est assez impressionnant.
Concernant l'IDE, j'ai toujours été fidèle à Emacs (même pour faire du Java ou du .Net) et j'attends toujours de voir un autre éditeur aussi extensible. Pour de l'édition de texte, je n'ai encore pas trouvé mieux. Je connais assez peu Eclipse et Visual Studio, donc je ne comparerai pas (la seule fois que j'ai essayé Eclipse, j'en ai gardé un souvenir de lenteur extrême -- mais c'est peut-être à cause du PC :)).
Pour la syntaxe, je la trouve vraiment lourde. Il suffit de faire  la comparaison avec n'importe quel langage décent, du code Java sera toujours au moins 4 fois plus long (cf par exemple http://en.wikipedia.org/wiki/Comparison_of_programming_languages#Expressive
ness).
 Alors, je veux bien croire qu'un bon IDE aide
en partie, mais ça restera toujours loin derrière un bon langage.
Oui, la syntaxe reste relativement claire (et c'est vrai que le C++ est très laid), mais le code reste très long. A chaque fois que je vois du code Java, j'ai l'impression que la majorité du code ne sert à rien. Déclarations d'interfaces, de classes abstraites... le pourcentage de code utile est bien inférieur à ce que l'on trouve dans le autre langages.
Pour le full objet, pourquoi ajouter 4 caractères ? Pour le "new " ? Pourtant, on pourrait s'en passer (cf ruby). Et ça apporterait tellement plus, au niveau de la cohérence et de la réutilisation de code.
Pour ce qui est de la doc, je ne suis pas d'accord avec toi. Je pense que ça vient surtout de l'habitude. A chaque fois que j'ai fait du Java, je me suis senti perdu. A chaque fois je galère pour faire des choses aussi simples que trier un tableau (selon une fonction).
Par exemple, comment traduire ce code F# en Java ?
[(4, 4, true); (3, 8, false); (7,5, true); (1, 3, true)] |> List.filter (fun (_,_,b) -> b) |> print_any
En français : mettre en dur dans le code une liste de triplets (entier, entier, booléen), n'en garder que les éléments dont le booléen est à vrai, et afficher cette liste ?
Est-on contraint de déclarer un nouveau type, de se taper des boucles, et d'écrire sa propre fonction d'affichage ?
 
"il faut l'envisager comme un ensemble de technologies étudiées pour être exploitées en synergie"
Je critique principalement le langage en lui-mêm, et non ce qu'il y a autour. Je considère que ce qui est autour (les bibliothèques) peut être implémenté de la même façon dans un autre langage. Alors que le cœur du langage ne pourra pas significativement évoluer (pour la compatibilité). C'est pourquoi je pense que tout le monde aurait à y gagner à utiliser un *langage* correctement conçu. A terme, c'est beaucoup plus prometteur.
Je préfère largement plus soutenir F# (alors que c'est encore une bêta et qu'il a encore des bugs) parce qu'il est extrêmement prometteur. De son côté Java, tout comme C++, est construit sur une base bancale. C'est dommage que les gens continuent à l'utiliser.
Concernant C#, je ne le maitrise pas, mais il me donne l'impression d'être mieux conçu. J'ai rencontré plusieurs fois des fonctionnalités qui m'ont agréablement surpris (j'ai pas d'exemple en tête, désolé). Mais surtout, il évolue beaucoup plus vite, et mieux. Regarde par exemple les fonctionnalités venant avec C# 3. A noter au passage ces ajouts ont été en partie influencées par F# (qui a servi de test). Du coup, ça ajoute une saveur fonctionnelle au langage (enfin des lambda expressions !)
http://fr.wikipedia.org/wiki/C_Sharp#Capacit.C3.A9s_introduites_dans_C.
23_3.0 

19:43 17/02/2007 - psyclik

Le problème, est qu'il n'y a pas d'autres langages aussi bien entourés que Java (toujours à ma connaissance et si ca existe, ca m'interesse ;p).
 
Pour .Net, je l'utilise pas mal aussi, et au final c'est vraiment un clone de Java + exploitation de la couche proprio Microsoft. C# est un clone de Java ou ils ont vu les 2/3 points chiants et les ont corrigés. Et .Net n'est pas portable. Et une installation d'application d'entreprise à échelle supérieure à celle du simple poste de travail nécessite des milles et des cents en infrastructure et licences.
La saveur fonctionnelle sera également intégrée à Java 7 (Dolphin).
 
Bref, je ne vois pas de problème :). 

23:58 17/02/2007 - LLB

Juste pour le troll Java / C#. Voici une liste de fonctionnalités qu'il manquait à Java : 
http://www.dotnetguru.org/articles/CSharpVsJava.htm#_Maintenant,_des_ch
oses
 
Le comparatif date un petit peu, et je crois que certaines fonctionnalités ont été ajoutées depuis. Mais quand même... Cela dit, je n'aime pas vraiment C# non plus (ça ressemble trop à Java :p). Ca reste trop verbeux, trop impératif, et on rencontre les mêmes limitations, notamment au niveau des structures de données.
 
Et surtout, devoir tout typer à la main... on se croirait revenus à la préhistoire ! :)

16:47 23/04/2008 - inconnu

J'ai vu dans un autre message que vous portez LISP aux nues ; ceci explique à lui seul le contenu de votre critique sur JAVA ;-)
 

> Rédiger un commentaire