août
2006
Il est parfois intérressant de voir ce qui se fait chez le voisin, afin de pouvoir faire une comparaison avec ce qui se fait à la maison (pour ceux qui ne l’aurait pas encore compris, je parle bien sûr de C# et de Java)
Or, il y a quelques jour de cela, j’ai lu l’article de Thomas LEBRUN concernant la prochaine version de C# : C# 3 et Linq.
Qu’y aura-t-il donc dans la prochaine monture de C# (dont on ne connait pas encore la date de publication) qui pourrait faire envie à un développeur Java ? Tel est la question que je me suis posé !
J’ai donc étudié de près ces nouvelles fonctionnalités, et en voici un petit aperçu de ces nouveautés et des impacts que cela pourrait avoir sur Java :
Variables locales typées implicitement
Cette nouvelle fonctionnalité permet de libérer les développeurs de la contrainte d’avoir à spécifier, à chaque fois, le type de leurs variables ou de leurs requête.
Le mot clef var
permet de définir une variable locales typées implicitement. C’est à dire que c’est le compilateur qui se chargera de ‘typer’ la référence de l’objet.
Ainsi, il est possible d’écrire le code suivant :
var x = 5;
var s = "Article C# 3 / LINQ";
var tab = new int [] { 1, 2, 3, 4, 5 };
tout en conservant la possibilité d’appeler la méthode spécifique du type exacte de la référence.
Le compilateur doit sans doute compiler cela de la même manière que le code suivant :
int x = 5;
String s = "Article C# 3 / LINQ";
int[] tab = new int [] { 1, 2, 3, 4, 5 };
L’intérêt m’a semblé peu évident à première vue, mais doit se relever fort intérressant lorsqu’il est utilisé avec des classes Generics qui sont très verbeuse. Ainsi, à la place d’écrire ceci :
List<Comparable<? extends Number>> list = new ArrayList<Comparable<? extends Number>>();
Ce serait pas mal de pouvoir écrire simplement :
var list = new ArrayList<Comparable<? extends Number>>();
On perd ici la notion d’abstraction de l’implémentation de la liste, mais on gagne en écriture et en lisibilité, et on s’évidente la déclaration redondante du typage !
Intégrer cela dans Java ?
Ce serait possible en ajoutant un mot-clef dans le langage (var en l’occurrence), ce qui n’est pas sans poser problème de compilation d’anciens programmes (en particulier si ce mot-clef était utilisé comme nom de variable). Toutefois cela a déjà été fait avec le mot-clef enum dans Java 5.0.
Mais il ne devrait pas y avoir d’incompatibilité au niveau du bytecode, car le compilateur se chargerait de remplacer le var par le type exact de la variable. Donc au final le bytecode serait strictement identique à celui que l’on écrirait actuellement.
Les méthodes d’extension
Les méthodes d’extensions vous permettent d’étendre les fonctionnalités offertes par un type, en définissant de nouvelles méthodes qui seront invoquées en utilisant la syntaxe normale d’appel d’une méthode.
Pour faire simple, il ne s’agit ni plus ni moins que la possibilité de masquer l’appel à une méthode static comme un appel de méthode membre d’une autre classe. Par exemple l’appel d’une méthode static du genre :
String s = "xxx";
AClassName.methodName(s);
Peut s’ecrire directement :
String s = "xxx";
s.methodName();
Le compilateur générera un code identique dans les deux cas (c’est à dire un appel à une méthode static).
Cette fonctionnalité me m’inspire guère, car le gain n’est pas si évident que cela et risque au contraire de déconcerter pas mal de monde…
Intégrer cela dans Java ?
Encore une fois, avec C# 3 c’est le compilateur qui fait tout le boulot, cela devrait donc être également possible en Java sans modifier le bytecode…
Méthodes anonymes et expressions lambdas
Il s’agit ici en partie d’une fonctionnalités de C# 2 puisque les méthodes anonymes y sont déjà présente. Elles permettent de déclarer une méthode en ligne comme on peut le faire pour les classes anonymes. On peut ensuite utiliser ces méthodes comme bon nous semble (un peu comme les pointeurs de fonction du C/C++).
Par exemple, pour reprendre l’exemple cité, une méthode peut accepter une méthode anonyme en paramètre, ce qui peut s’écrire de la manière suivante :
list.FindAll( delegate(int i) { return i > 0;} );
Et les expressions lambdas de C# 3 ne constitue ni plus ni moins qu’une nouvelle manière d’écrire des méthodes anonymes de manières plus concises :
list.FindAll( (int i) => i > 0; );
Cela permet de passer simplement une méthode en paramètre d’une autre méthode (entre autre).
En Java il n’y a pas d’équivalent actuellement, et on doit se contenter de passer une classe implémentant une interface possédant une méthode… Ce qui revient souvent à écrire un gros paquet de code pour pas grand chose, comme par exemple pour passer une tâche à l’EDT :
SwingUtilities.invokeLater( new Runnable() {
public void run() {
/* code ici */
}
});
Intégrer cela dans Java ?
Ca tombe bien : je suis tomber sur une étude concernant la possibilité d’intégrer cela dans Java : Closures for Java [PDF], signé par Gilad Bracha, Neal Gafter,Peter Ahé et même James Gosling, le père de Java !
On y retrouve une syntaxe assez proche, ce qui permettrait d’écrire des choses du style :
SwingUtilities.invokeLater( () { /* code ici */ } );
Les impacts sont toutefois assez important et impliquerais sûrement une incompatibilité du bytecode en plus de la modification du code-source (lire le document pour plus de détail). Mais c’est une possibilité fort intérressante !
Les initialiseurs d’objets
Il s’agit d’une syntaxe proche de la déclaration de tableau en ligne, mais qui permet de fixer des attributs d’une instance classe grâces à ses méthodes setters.
Par exemple le code suivant :
User u1 = new User { FirstName = "Thomas", LastName = "LEBRUN", Age = 24 };
serait l’équivalent de celui-ci :
User u1 = new User();
u1.FirstName = "Thomas";
u1.LastName = "LEBRUN";
u1.Age = 24;
(Note pour les Javaistes : en C# l’opérateur = sur les attributs effectue un appel implicite à la méthode setter de ce dernier).
Je suis ici une nouvelle fois assez sceptique. L’intérêt n’est pas vraiment flagrant à mes yeux pour de simple objet.
Toutefois, il est possible de faire la même chose avec des collections de la même manière que pour des tableaux, et là cela devient bien plus intérressant :
List<int> MyListOfInteger = new List<int>{1, 2, 3, 4, 5};
Est l’équivalent de
List<int> MyListOfInteger = new List<int>();
MyListOfInteger.Add(1);
MyListOfInteger.Add(2);
MyListOfInteger.Add(3);
MyListOfInteger.Add(4);
MyListOfInteger.Add(5);
Intégrer cela dans Java ?
Le compilateur peut très bien se charger de générer un bytecode compatible en « transformant » la syntaxe simplifié en sa version « longue ». Il est donc possible d’intégrer cela sans trop d’impact.
Les types anonymes
Ce point rejoint un peu le premier concernant les variables locales typées implicitement, en le couplant avec des classes anonymes.
En effet, actuellement en Java (et sûrement en C# également), pour pouvoir être utilisée, ces classes doivent étendre un classe ou implémenter une interface connu, car les nouvelles méthodes et attributs ne peuvent pas être utilisé directement dans le bloc parent (sauf en passant par la réflection).
Pour reprendre l’exemple qui défini un type avec 3 attributs :
var MyClass = new { PremierChamp = 1, SecondChamp = "Ma Chaîne", TroisiemeChamp = new List<int>(new int [] { 1, 2 , 3, 4, 5 }) };
qui reste parfaitement accéssible depuis le bloc parent, c’est à dire que l’on peut très bien avoir ensuite le code suivant :
Console.WriteLine(MyClass.PremierChamp);
Console.WriteLine(MyClass.SecondChamp);
foreach(int i in MyClass.TroisiemeChamp)
{
Console.WriteLine(i);
}
Toutefois, l’utilisation d’une inner-classe permet déjà de faire la même chose (si ce n’est que la classe est nommé explicitement). L’intérêt est bien faible à mes yeux…
Intégrer cela dans Java ?
Cela nécessite bien sûr l’intégration du mot-clef var, mais cela ne devrait pas poser de problème au niveau du bytecode mais simplement permettre au compilateur de vérifier les accès aux attributs et méthodes du type exact et non pas de la classe parente ou de l’interface implémenté… mais est-ce bien nécessaire ?
Le projet LINQ
Enfin, ce qui constitue sûrement la grande évolution de C# : LINQ (Language INtegrated Query)
LINQ est un langage de requête universel, dont la syntaxe est très proche du SQL, à la différence qu’il permet d’interroger aussi bien une base de données qu’un document XML ou une collection d’objet en mémoire ! Il a toutefois le gros avantages d’être complètement intégrée au langage et donc de bénéficier de la bienveillance du compilateur qui veillera à sa syntaxe et à sa cohérence !
Par exemple, on peut ainsi écrire une instruction de la manière suivante :
IEnumerable<int> paire = from number in tab
where number % 2 == 0
select number;
- IEnumerable semble correspondre à note interface Iterable, et permet donc d’itérer sur une liste d’élément. Elle est destiné à récupérer les données renvoyées par la requête.
- La requête indique de ne récupérer que les valeurs de number qui sont paires (grâce au modulo 2) parmi tous les éléments de tab.
Et c’est là que cela devient intérressant : tab peut aussi bien être un objet en mémoire, qu’un objet représentant une table SQL ou un fichier XML. L’accès aux données est totalement transparent !
Ce « mini-langage » reconnaît plus de 40 opérateur, dont de nombreux viennent directement du SQL (min, max, sum, etc.) et permet donc d’effectuer à peu près tous ce qu’il est possible de faire en SQL.
Intégrer cela dans Java ?
Je dirais OUI tout de suite vu que les possibilités semblent nombreuses (lire l’article pour plus de détail).
Toutefois, cela pourrait demander d’importante modification du bytecode.
Je n’ai pas connaissance d’un équivalent en Java, mais cela pourrait vraiment être très intérréssant !
Ou mieux : pourquoi pas intégrer cela directement à l’API de scripting qui apparaîtra dans Java SE 6 afin d’avoir une vrai vérification des scripts à la compilation et une meilleure intégration (ce qui n’est pas le cas actuellement puisque les scripts doivent être contenu dans un objet String ou dans un fichier séparé).
6 Commentaires + Ajouter un commentaire
Tutoriels
Discussions
- Possibilité d'accéder au type générique en runtime
- Recuperation du nom des parametres
- Difference de performances Unix/Windows d'un programme?
- Classes, méthodes private
- [REFLEXION] Connaitre toutes les classes qui implémentent une interface
- L'apparition du mot-clé const est-il prévu dans une version à venir du JDK?
- Définition exacte de @Override
- jre 1.5, tomcat 6.0 et multi processeurs
- [ fuite ] memoire
Pourquoi essayer d’imiter un langage inimitable comme le SQL avec « LINQ » ?
Oracle devrait directement intégrer le SQL standard (du moins les fonctionnalités de base avec les jointures internes/externes et les fonctions d’agrégats + TO_CHAR() & TO_DATE() ) dans Java.
Bon, après un rapide survol (j’suis au boulot tout de même), je dirai que la principale force de Linq sera pour l’interrogation de listes d’objets. Là, le langage de requêtage est vraiment fort, permettant des choses fort sympathiques en peu de lignes.
Pour les BDDs, il faudra déjà toute la couche ADO (qui, si générée automatiquement, ne fait que dupliquer la structure de la BD, très verbeusement, là où Hibernate l’est bien moins), puis seulement, sur les objets ADO, on pourra faire des requêtes franchement proche du SQL.
Pour le XML, il n’y a qu’un seul exemple de requête, alors oui ça apportera des choses mais rien de fou en soi.
L’autre grand intérêt de linq, à mes yeux, est la vérification de la requête à l’écrite, vu que tout se fait sur des objets on a alors la complétion, la vérification syntaxique et tout ce qui va bien.
Après, reste encore à voir les perfs, mais bon, je ne m’inquiète pas trop, vu la puissance dispo de toutes façons…
M’enfin, s’il y a autant de langage que de type de données derrière, ça n’est plus, à mons sens, un langage de requête unifié, c’est plutot 3 langages différents accessibles via une interface unique non ?
Ce qui est soi peut être pas mal, enfin, ça fait toujours 3 langages différents derrière, et sans doute 3 façons d’accéder aux données (vu que déclarer une connexion BDD ne se fait pas de la même façon qu’ouvrir un flux RSS et/ou un fichier XML).
Bon, j’viens de télécharger le pdf présentant la chose en détails, je regarde de plus près
>> Pour ma part, je me demande un peu comment cela sera géré dans les détails.
Je ne pense pas qu’il y ait quelque chose d’extraordinaire dans les détails. Il doit tout simplement y avoir trois implémentations différentes :
Pour les BD en utilisant ADO (qui doit être l’équivalent de JDBC pour Java).
Pour XML en utilisant XPath (ou un langage du même style).
Et pour les objets surement via la reflection.
Le gros intérêt est d’avoir une interface et un langage de requête unifié (ou presque car il doit surement y avoir quelques spécificité) qui s’intègre totalement au langage de base.
Pour ma part, je me demande un peu comment cela sera géré dans les détails.
En effet, entre le spécifique à l’XML (les attributs des balises XML, les balises comprises dans d’autres balises etc…), le spécifique base de données (contraintes d’intégrité, problème de persistance, problème des lectures/ecritures) et le spécifique objet (méthodes& données privées/publiques, objet avec un attribut étant un autre objet, problèmatique du multithreading…), au final je me demande comment une seule façon d’accéder à tout cela peut fonctionner efficacement. Vous avez des éléments de réponse ?
Linq est un formidable projet reste à voir quand l’implémentation sera finie et quel seront les perfs.
Je pense qu’il faudra être un peu patient mais coté perf je suis pas trop inquiet