"Seuls les imbéciles ne changent pas d'avis". Cette adage prend toute sa valeur avec l'annonce officielle de Sun visant à créer une JSR (Java Specification Request) chargée de plancher sur les prochaines évolutions du langage Java. Autant dire que cette annonce revêt une importance capitale dans le duel que se livre actuellement Sun et Microsoft. DotNetGuru a déjà eu l'occasion à maintes reprises de mettre en avant les nombreuses qualités de C# à travers notamment des comparaisons avec Java. Les conclusions ressortant de ces différentes analyses sont généralement identiques. Contrairement à C# plus riche et plus complet, Java souffre de quelques lacunes qui tendent à le fragiliser. Pour résumer, les griefs à son encontre sont :
l'absence de mécanismes permettant de traduire automatiquement les types primitifs (int, float, ...) en objets ou boxing/unboxing.
l'absence du mot-clé enum présent dans énormément de langages
l'absence de structure de contrôle intégrée de type foreach permettant d'itérer dans une collection d'objets
La généricité qui lui fait cruellement défaut, etc ...
Les références constantes qui évitent de passer systématiquement par l'ordre "object.clone()" peu performant.
Le mot-clé ReadOnly, les Indexeurs, l'exécution SideBySide prenant en compte le versionnage de classes, etc ...
La plupart de ces fonctionnalités ont été à maintes reprises demandées par la communauté Java sans réellement recevoir d'écho de la part de Sun plus préoccupé à faire évoluer la norme J2EE. Aujourd'hui, L'avènement de la plateforme .NET a indéniablement donné un coup de fouet dans les projets de l'éditeur californien qui s'est enfin résigné à changer de fusil d'épaule. Plus que jamais, il est aujourd'hui nécessaire de redonner ses lettres de noblesses à Java pour espérer challenger ce sérieux concurrent que représente C#. Et ce, avec l'obligation d'assurer une compatibilité ascendante avec les machines virtuelles existantes. Tout un programme.
Alors que jusqu'ici Microsoft se voyait reproché d'avoir copié certains éléments de l'architecture Java, aujourd'hui c'est au tour de Sun de s'inspirer des fonctionnalités de C# et de la plateforme .NET à travers cette nouvelle proposition d'évolution. Le but de cet article sera donc d'étudier avec vous l'ensemble de ces nouvelles fonctionnalités tout en les comparant avec leur équivalent C#, lorsqu'ils existent.
Les énumérations font partie des éléments les plus importants dans la JSR de Sun. Encore aujourd'hui la plupart des développeurs font appel aux classes pour implémenter la notion d'énumérations à l'aide de constantes. Dans la pratique, il s'avère que cette forme d'écriture posent certains problèmes en terme de complexité et de sécurité des types. Voici ce que la JSR propose dans ce domaine :
public enum
Feu { rouge, orange, vert}
La notation est beaucoup plus naturelle et les bénéfices apportés par cette fonctionnalité sont nombreux :
Comme en C#, la déclaration d'un Enum se rapproche de la déclaration d'une classe ordinaire. Premièrement, les types Enum sont finales et possèdent des membres publiques représentant chaque élément de la liste. Les Enum possèdent une méthode toString(), hashCode() et equals() suivant le même principe que les méthodes habituelles. Un Enum implémente l'interface Comparable et Serializable mais ne peut être clonée (Clonable).
Ce choix d'implémentation ne s'est pas fait au hasard, utiliser des classes Java pour représenter les Enums est un moyen efficace pour garder une compatibilité avec les machines virtuelles existantes. Quant aux compilateurs, ils seront amenés à subir des adaptations mineures afin d'intégrer cette nouvelles syntaxe.
Le Boxing fait partie des nombreux atouts appréciables proposées aujourd'hui par le langage C# et qui contribuent indéniablement à sa notoriété. Cette fonctionnalité consiste à alléger le travail du développeur dans le cas de conversion entre type "primitif" et référence d'objet. Prenons l'exemple de l'insertion d'un entier ou d'un flottant dans une collection. Les entier ou les flottant en Java étant considérés comme des types de base ou type de valeur, il est donc nécessaire de faire appel à une classe intermédiaire chargée de jouer le rôle de wrapper pour la valeur en question. Cette opération souvent fastidieuse est la source d'erreurs fréquentes et s'avère contre-productif pour le développeur. L'Autoboxing consiste donc à convertir automatiquement le type en question lorsqu'une affectation ou une invocation de méthode est réalisée. L'exemple suivant implémenté en C# illustre le concept :
static void
BoxMe()
{
int i = 123;
object o = i;
}
Ce code source lors de la phase de compilation produit le résultat suivant en MSIL :
.method private hidebysig static
void BoxMe() il managed
{
// Code size
12 (0xc)
.maxstack
1
.locals (int32 V_0,
class System.Object V_1)
IL_0000:
ldc.i4.s 123
IL_0002: stloc.0
IL_0003: ldloca.s
V_0
IL_0005: box [mscorlib]System.Int32
IL_000a: stloc.1
IL_000b: ret
}
// end of method BoxTest::BoxMe
Vous aurez remarqué la présence de l'ordre "box" dont le rôle est d'assurer la gestion du Wrapper d'entier (System.Int32)
Comment sera implémenté l'autoboxing dans Java ? Pour l'heure, la JSR actuelle ne fournit aucun cadre strict quant à son implémentation. Les propositions énoncées reprennent essentiellement les concepts existants dans C# excepté l'autounboxing qui consiste à réaliser l'opération inverse. En fait, le document de JSR s'attache surtout à décrire les différents scénarios de conversion des types sans aborder les détails d'implémentation laissé à l'appréciation du groupe d'experts.
Enfin, il a noter que Sun annonce d'ores et déjà une modification de la JLS (Java Langage Specification) pour prendre en compte l'autoboxing.
Aujourd'hui, l'une des solutions les plus utilisées pour parcourir une collection consiste à faire appel à un itérateur de la manière suivante :
for (Iterator i =
c.iterator(); i.hasNext(); )
{ String s = (String) i.next(); ...
}
WithoutGenerics.java
Si à l'avenir, avec la présence des templates ou generics cette écriture s'allègera légèrement, il n'en demeure pas moins qu'elle pourrait être réduite à sa plus simple expression.
for (Iterator<String>
i = c.iterator(); i.hasNext(); )
{ String s = i.next(); ... }
WithGenerics.java
Voici ce que propose la JSR pour intégrer dans Java 1.5 une syntaxe se rapprochant de l'ordre foreach de C# :
for (String s :
c)
{ (...) }
Vous conviendrez qu'il est difficilement possible de faire plus concis. Sun a préféré éviter l'ajout un mot-clé supplémentaire pour la raison suivante : "Traditionally this is done using a foreach keyword, but it seems unnecessary and counterproductive to add a keyword at this late date". S'ils ne souhaitent pas rajouter de mot-clé supplémentaire au langage, la raison, elle, paraît quelque peu hasardeuse.
De plus, ce changement nécessitera la modification de la classe Collection pour implémenter une future interface Iterable. Cela signifie que seules les objets respectant cette interface pourront être utilisés avec l'ordre foreach. Voici la nouvelle interface proposée dans la JSR :
package
java.lang;
public
class Iterable {
/**
*
Returns an iterator over the elements in this collection.
There are no
*
guarantees concerning the order in which the elements are returned
*
(unless this collection is an instance of some class that provides a
*
guarantee).
*
*
@return an Iterator over the elements in this collection.
*/
SimpleIterator iterator();
}
Par ailleurs, une autre interface fait son apparition : ReadOnlyIterator :
using
java.lang;
public
interface ReadOnlyIterator {
/**
*
Returns true if the iteration has more elements. (In other
*
words, returns true if next would return an element
*
rather than throwing an exception.)
*
*
@return true if the iterator has more elements.
*/
boolean hasNext();
/**
*
Returns the next element in the iteration.
*
*
@return the next element in the iteration.
*
@exception NoSuchElementException iteration has no more elements.
*/
Object next();
}
Cette nouvelle interface est destinée à séparer les itérateurs modifiables et les itérateurs en lecture seule. Pour cela, la version actuelle de java.util.Iterator sera modifiée pour dériver de java.util.ReadOnlyIterator.
Si nous jetons un rapide coup d'oeil sur le Namespace System.Collections de C#, il nous indique que la classe abstraite CollectionBase équivalente de l'interface java.util.Collection de Java implémente IEnumerable. Avec une différence de taille, la classe ReadOnlyCollectionBase existe dans le Framework .NET mais pas sous la forme d'un itérateur comme c'est le cas dans cette JSR. On peut donc légitimement penser que Sun s'en est inspiré. Au passage, voici une très bonne source de discussions : l'implémentation des collections en lecture seule doit-elle être laissée à l'appréciation des itérateurs comme en Java ou des collections comme en .NET ? A méditer.
Enfin, il est à noter que ce nouvel ajout du langage ne nécessitera aucune modification de la machine virtuelle, seul le compilateur est impacté du fait de la génération de code induite par la nouvelle forme d'écriture. Par ailleurs, il est clair que cette JSR s'inspire énormément de l'actuelle conception du Framework .NET.
L'import "static" fait partie des nouveautés les plus originales dans cette JSR. Ce concept permet de s'abstraire de l'écriture quelque peu fastidieuse qui consiste à systématiquement préfixer un attribut ou une méthode statique par sa classe. La JSR propose donc l'écriture suivante pour résoudre ce problème :
import static TypeName.Identifier ; ou import static Math.abs ;
Destiné à importer dans l'unité de compilation courante la méthode abs() spécifiée dans la classe ou l'interface TypeName, en l'occurence ici la classe Math. Une autre forme d'écriture plus générale est également proposée :
import
static Feu.* ;
(...)
public void MyMethod() {
if (Feu.equals(ROUGE))
...
}
(...)
}
Permet d'importer l'ensemble des types statiques de la classe ou de l'interface TypeName. Cette solution implique simplement la réécriture du code généré par le compilateur afin de qualifier entièrement les méthodes ou attributs importés. Cela permet encore une fois de faire l'économie de modifications ou remise en cause de la machine virtuelle menant à une rupture de compatibilité ascendante.
Pourquoi une telle fonctionnalité ? Tout d'abord, en terme de concision d'écriture, cela a un impact direct sur l'utilisation intensive de variables statiques. Le meilleure exemple est certainement la classe java.lang.Math qui contient des méthodes telles que Math.abs(), Math.sqrt() ou Math.max(a,b). Pourquoi préfixer la classe alors qu'un ordre import est spécifié. Par ailleurs, dans le cadre des énumérations (Enum), si cette proposition de spécification est adoptée, à l'avenir il sera possible d'utiliser l'écriture suivante :
import
static Feu.* ;
public
class Foo {
(...)
public enum
Feu (ROUGE, VERT, ORANGE) ;
(...)
}
import
static Math.* ;
public
class Foo {
(...)
Sqrt(3);
Abs(2);
(...)
}
Cette fonctionnalité est tout à fait intéressante dans la mesure ou c'est la première fois qu'une caractéristique du langage Java n'a pas d'équivalent en C#.
Ce petit récapitulatif des nouvelles fonctionnalités proposées dans la prochaine version de Java est intéressant à plusieurs titres. Non seulement il nous donne un état sur ce que sera l'avenir prochain de Java mais il tente également de les replacer dans leur contexte équivalent en C#.
Si cette JSR se confirme, le fossé entre les deux langages va se réduire pour ne laisser place qu'à de légers points de différentiation. Mais pour l'heure, C# a toujours une longueur d'avance et il faudra patienter encore quelques temps pour voir apparaître des caractéristiques telles que le versionning des classes Java, l'ordre ReadOnly, les Indexeurs, etc ... En attendant, pourquoi Microsoft ne proposerait-il pas l'ordre using static au menu des prochaines spécifications de C# ;-). Finalement la concurrence est une bonne chose.
Auteur : Sami JABER
Copyright : DotNetGuru © Décembre 2002
Pour en savoir plus ...
Le namespace Collection du Framework .NET : source MSDN
La JSR 201 proposant ces nouvelles fonctionnalités