• Aucun résultat trouvé

On a souvent besoin d’ensembles finis de donn´ees conventionnelles, comme { nord, sud, est, ouest } ou { lundi, mardi, mercredi, ... dimanche }, etc. Quel que soit le domaine d’application, ces donn´ees partagent certains traits caract´eristiques :

1. Ces donn´ees sont tr`es symboliques et naturellement munies de noms significatifs. C’est pourquoi il est regrettable de les repr´esenter `a l’aide de simples nombres, comme on le fait quand on n’a pas d’autre moyen : 0 pour nord, 1 pour sud, 2 pour est, etc.

2. Ce sont des donn´ees atomiques – comme des nombres –, ce qui doit leur donner un faible encombrement et permettre un traitement tr`es rapide. C’est pourquoi il est regrettable de les repr´esenter par des chaˆınes de caract`eres, comme on le fait parfois : "nord" pour nord, "sud" pour sud, etc.

3. Ces donn´ees constituent des ensembles finis, souvent de taille r´eduite, ce qui doit permettre certaines op´erations et certaines optimisations – par exemple, pour la repr´esentation de sous-ensembles de l’en- semble de telles donn´ees – auxquelles il faut renoncer si on repr´esente ces donn´ees par des nombres ou des chaˆınes de caract`eres.

4. Elles constituent des ensembles disjoints d’avec les autres types de donn´ees (un point cardinal n’est pas interchangeable avec un jour de la semaine !). Cela devrait permettre, de la part du compilateur, un puissant contrˆole de type qu’on n’aura pas si on repr´esente ces donn´ees par des nombres ou des chaˆınes.

En Java, jusqu’`a la version 1.4, on impl´ementait presque toujours les donn´ees symboliques par des suites de constantes num´eriques :

public static final int NORD = 0; public static final int SUD = 1; public static final int EST = 2; public static final int OUEST = 3;

Cette mani`ere de faire est en accord avec les deux premi`eres propri´et´es ci-dessus, mais pas avec les deux suivantes. En effet, rien ne dit au compilateur que les constantes NORD, SUD, EST, OUEST constituent la totalit´e d’un type et, surtout, ces constantes ne forment mˆeme pas un type : le compilateur ne pourra pas nous aider si par inadvertance nous affectons la valeur NORD `a une variable jourDeLaSemaine (dont les valeurs attendues LUNDI, MARDI, etc., sont ´egalement repr´esent´ees par des constantes enti`eres).

Java 5 corrige ces d´efauts en introduisant les nouveaux types ´enum´er´es ou enums, expliqu´es ci-apr`es.

12.1.1 Enums

La d´eclaration d’un type ´enum´er´e se compose au moins des ´el´ements suivants (cela s’enrichira par la suite) :

qualifieurs enum identificateur { identificateur , identificateur , ... identificateur } Deux exemples :

public enum PointCardinal { NORD, SUD, EST, OUEST }

Note. Les deux d´eclarations pr´ec´edentes introduisant des types publics, elles doivent ˆetre ´ecrites dans deux fichiers s´epar´es, respectivement nomm´es PointCardinal.java et JourSemaine.java.

Exemples d’utilisation ult´erieure :

PointCardinal direction = PointCardinal.NORD; ...

JourSemaine jourDePresence = JourSemaine.MERCREDI;

Notez bien que la d´eclaration d’un type enum n’est pas un simple artifice pour donner des noms ex- pressifs `a une poign´ee de nombres entiers. Les d´eclarations pr´ec´edentes cr´eent bien deux types nouveaux, PointCardinal et JourSemaine, distincts entre eux et distincts de tous les autres types, dont l’emploi pourra ˆetre finement contrˆol´e par le compilateur.

Pour faire comprendre la nature et les propri´et´es des valeurs d’un type enum disons pour commencer (car en fait c’est plus riche que cela) que la d´eclaration

public enum PointCardinal { NORD, SUD, EST, OUEST } a tout l’effet qu’aurait eu la d´eclaration suivante :

public class PointCardinal {

public static final PointCardinal NORD = new PointCardinal(); public static final PointCardinal SUD = new PointCardinal(); public static final PointCardinal EST = new PointCardinal(); public static final PointCardinal OUEST = new PointCardinal();

private PointCardinal() {} // pour emp^echer la cr´eation d’autres instances }

M´ethodes des types ´enum´er´es

Un type ´enum´er´e apparaˆıt donc comme une classe dont les seules instances sont les valeurs du type ´enum´er´e. En fait, tout type ´enum´er´e est sous-classe de la classe java.lang.Enum, qui introduit quelques m´ethodes bien utiles :

String name(), String toString()

Ces deux m´ethodes renvoient la valeur en question, exprim´ee sous forme de chaˆıne de caract`eres ; par exemple PointCardinal.NORD.name() est la chaˆıne "NORD". La seconde est plus int´eressante car elle peut ˆetre red´efinie. Cela s’´ecrit comme ceci :

public enum PointCardinal { NORD, SUD, EST, OUEST; public String toString() {

return "<" + super.toString().toLowerCase() + ">"; }

}

Dans ces conditions, l’instruction

System.out.println(PointCardinal.OUEST); produit l’affichage de

<ouest>

static ´enum´eration valueOf(String nom)

Renvoie la valeur du type ´enum´er´e dont le nom – ´ecrit exactement comme dans la d´efinition du type ´enum´er´e – est donn´e. Par exemple, PointCardinal.valueOf("EST") vaut PointCardinal.EST. static ´enum´eration[] values()

Renvoie un tableau contenant toutes les valeurs du type ´enum´er´e. Par exemple, le code PointCardinal[] directions = PointCardinal.values();

for (int i = 0; i < directions.length; i++) System.out.print(directions[i] + " "); affiche le texte NORD SUD EST OUEST .

int ordinal()

Renvoie un entier qui exprime le rang de la valeur dans le type : PointCardinal.NORD.ordinal() vaut 0, PointCardinal.SUD.ordinal() vaut 1, etc.

La transformation r´eciproque est facile `a obtenir : PointCardinal.values()[0] vaut PointCardi- nal.NORD, PointCardinal.values()[1] vaut PointCardinal.SUD, etc.

12 JAVA 5 12.1 Types ´enum´er´es

Aiguillages command´es par des types ´enum´er´es

Bien qu’elles ne soient pas des nombres ou des caract`eres, les valeurs des types ´enum´er´es peuvent servir `a piloter des aiguillages. Par exemple, supposons avoir la d´eclaration

public enum CategorieVehicule { BERLINE, COUPE, BREAK; }

voici le code d’un tel aiguillage : ...

CategorieVehicule categorie; ...

switch (categorie) { case BERLINE:

code correspondant au cas o`u categorie vaut CategorieVehicule.BERLINE break;

case COUPE:

code correspondant au cas o`u categorie vaut CategorieVehicule.COUPE break;

case BREAK:

code correspondant au cas o`u categorie vaut CategorieVehicule.BREAK break;

default:

throw new AssertionError("\nErreur de programmation: cas " + categorie + " non trait´e dans switch");

}

Notez que, puisque le switch est command´e par une expression de type CategorieVehicule, `a l’int´erieur de celui-ci le compilateur laisse ´ecrire BERLINE, COUPE ou BREAK au lieu de CategorieVehicule.BERLINE, CategorieVehicule.COUPE ou CategorieVehicule.BREAK.

Notez ´egalement que l’utilisation de la clause default, comme ci-dessus, est recommand´ee. En proc´edant ainsi, si plus tard on ajoute des valeurs au type ´enum´er´e en oubliant d’ajouter les case correspondants dans les switch concern´es, on en sera pr´evenu (h´elas, on ne sera pr´evenu qu’`a l’ex´ecution, on aurait sans doute pr´ef´er´e l’ˆetre durant la compilation...).

12.1.2 Dictionnaires et ensembles bas´es sur des types ´enum´er´es

Dictionnaires. Le nouveau type EnumMap (en r´ealit´e c’est une classe param´etr´ee mais pour commencer nous passerons cet aspect sous silence – voyez la fin de cette section) a ´et´e introduit pour permettre la construction de tables associatives dont les cl´es sont les valeurs d’un type ´enum´er´e, avec optimisation de l’espace et du temps d’acc`es. Cela s’emploie comme ceci :

Map permanence = new EnumMap(JourSemaine.class); ...

permanence.put(JourSemaine.MARDI, "M. Jack Palmer");

permanence.put(JourSemaine.JEUDI, "Mme Raymonde Bidochon"); ...

JourSemaine jour; ...

String qui = (String) permanence.get(jour); if (qui != null)

System.out.println("le permanent de " + jour + " est " + qui); ...

Ensembles. De mani`ere analogue `a EnumMap, le type EnumSet (encore une classe param´etr´ee, voyez la fin de cette section) a ´et´e introduit en vue de la d´efinition d’ensembles compacts et efficaces101 dont les

´el´ements sont les valeurs d’un type ´enum´eration donn´e.

Les ensembles EnumSet supportent, comme toutes les collections, les op´erations fondamentales contains (test d’appartenance), add et remove (ajout et suppression d’un ´el´ement), plus quelques op´erations de fa- brication particuli`erement commodes :

101On nous assure que ces ensembles sont r´ealis´es avec des tables de bits, ils sont donc aussi peu encombrants et d’une

manipulation aussi efficace que les ensembles qu’on peut r´ealiser avec des ´el´ements repr´esent´es par des puissances de 2 et les op´erateurs de bits & et |.

static EnumSet of(Object premierElement, Object... autresElements)

Construction d’un ensemble form´e des ´el´ements indiqu´es (il s’agit d’une m´ethode avec nombre variable d’arguments, cf. § 12.3.3).

static EnumSet allOf(Class typeElements)

Construction d’un ensemble form´e de tous ´el´ements du type ´enum´er´e indiqu´e. static EnumSet complementOf(EnumSet s)

Construction d’un ensemble form´e de tous les ´el´ements du type ´enum´er´e en question qui ne sont pas dans l’ensemble indiqu´e.

Exemple : ...

EnumSet joursDePresence

= EnumSet.of(JourSemaine.LUNDI, JourSemaine.MERCREDI, JourSemaine.VENDREDI); ... joursDePresence.add(JourSemaine.MARDI); joursDePresence.remove(JourSemaine.VENDREDI); ... JourSemaine jour; ... if (joursDePresence.contains(jour))

System.out.println("ok pour " + jour); ...

Note. Nous ne l’avons pas fait apparaˆıtre dans les lignes pr´ec´edentes, pour ne pas les alourdir, mais en r´ealit´e EnumMap et EnumSet sont des classes param´etr´ees (cf. § 12.5) et leurs d´eclarations compl`etes prennent les formes – assez troublantes – suivantes :

class EnumSet<E extends Enum<E>> { ... } class EnumMap<K extends Enum<K>, V> { ... }