C# : 1 – Java : 0
Le samedi 5 avril 2008 à 11:02 - Lien permanent
À l'occasion de quelques amusements en C# effectués en ce moment...
Il y a deux ans, je m'étais plaint des generics de Java. En effet, le paramétrage des types est effacé à la compilation, ce qui rend certaines opérations impossibles à faire. Par exemple la méthode toArray
de l'interface List
doit recevoir en paramètre un tableau du type paramètre, car celui-ci est oublié à la compilation. Mais hors de question de pouvoir écrire :
public static <T> T[] truc(T a) { List<T> liste = new ArrayList<T>(); liste.add(a); return liste.toArray(new T[] {}); }
L'expression soulignée est incorrecte, parce que justement, à l'exécution la JVM ne connaît pas T
... Problème insoluble.
Dans .Net, la CLR n'oublie pas le type des paramètres. Dans une méthode paramétrée par <T>
, on peut donc parfaitement écrire une expression du type new T[] { ... }
. Et la méthode ToArray
n'a donc pas besoin qu'on lui fournisse un tableau du type paramètre. Le code ci-dessus, incorrect en Java, s'écrit de façon élégante en C# :
public static T[] truc<T>(T a) { List<T> liste = new List<T>(); liste.Add(a); return liste.ToArray(); }
Notons que pour ne rien simplifier, les concepteurs de Java et C# ont forcément trouvé le moyen de faire différer la syntaxe au niveau de la place du paramètre <T>
dans la déclaration de fonction... Mais clairement, les generics me semblent avoir été traités à la légère en Java. Le système de l'erasure des informations de type, même s'il est suffisant pour assurer la type safety dans la plupart des cas, et même s'il permettait de garder la JVM inchangée, conduit à des solutions vraiment épouvantables (cast + @SuppressWarnings("unchecked")
) dans des cas un peu complexes...
Commentaires
> Mais clairement, les generics me semblent avoir été traités à la légère en Java
Tout à fait d'accord. Si j'étais mauvaise, je pourrais presque me laisser aller à dire que Sun a voulu vite fait récupérer les bonnes idées de C# 2 pour les intégrer à Java 5... Quant à l'erasure, je suis pas sûr d'en voir l'intérêt. Quel est l'intérêt pour le développeur que la JVM n'ait pas eu à changer pour intégrer les generics? De toute façon si on veut utiliser les generics, il faut passer à Java 5 et donc à la JVM et au JRE correspondants, non ?
Je savais pas que la syntaxe Java était différente pour les generics... J'ai bien peur que mon jugement soit quelque peu biaisé en faveur de C#, mais la syntaxe C# n'est-elle pas plus naturelle ? Après tout pour utiliser la fonction truc, on écrit bien truc<T>(a), non ?
Bonne question ! En Java on écrit simplement
truc(a)
car il y a un mécanisme complexe d'inférence de type à la compilation...Arg, oui c'est vrai, en C# aussi; mais la syntaxe "canonique" c'est ça.
Bon alors un autre exemple:
Dans ce cas le compilateur ne peut pas inférer T; puisqu'il n'apparaît pas parmi les paramètres.
Tu es donc obligé d'écrire quelque chose comme ça:
D'où ma question: la syntaxe C# n'est-elle pas plus naturelle ?
PS: Comment on fait pour avoir du texte formaté joliment dans les commentaires ? J'suis sûr que ça rendrait mon commentaire bien plus percutant :)
Oui je suis d'accord, la syntaxe est plus cohérente vis-à-vis du reste de la syntaxe du C#... Je ne crois pas que la syntaxe "canonique" existe en Java, j'imagine en raison de l'erasure : dans une fonction que tu appelles par
doStuff<T>()
, cela doit vouloir dire que quelque-part, tu dois créer ou aller récupérer un objet de typeT
, ce qu'on ne peut pas faire en Java... Donc la syntaxe de C# est bien cohérente, mais celle de Java n'est pas non plus incohérente ;-)Pour les commentaires, il ne me semble pas que ce soit possible avec la version de Dotclear que j'utilise... Pour la peine j'ai formaté ton commentaire moi-même ;-)
> Pour la peine j'ai formaté ton commentaire moi-même
Alors ça c'est gentil :-)
Pour en revenir aux generics, je suis vraiment surpris qu'on ne puisse pas spécifier le type à l'utilisation.
Par exemple, ce genre de code ne compile pas en C#:
public static void doStuff<T>(T myArg1, T myArg2)
{
...
}
static void Main()
{
doStuff(1, 2.4);
}
Puisque là le compilateur ne sait pas trop si il doit utiliser doStuff<int> ou doStuff<double>. C'est donc au développeur de l'indiquer explicitement.
En java, à ma grande surprise, le même programme compile sans problème. Tout se passe en fait comme si on avait écrit doStuff<object> en C#, si j'ai bien compris... Mais à ce moment-là cette fonction est strictement sans intérêt, puisqu'elle est équivalente à doStuff(Object,Object), vu que "grâce à" l'erasure, on perd toute information sur les types des paramètres.