P
'
t
i
t
e
C
h
a
t
t
e
 
spacer~ CONFUCIUS SAY: MAN WHO RUN IN FRONT OF CAR GET TIRED Articles | Connexion
 
~GRAIN DE SEL

 Classes et objets immuables
27/03/2006 - 11:02

Les developpeurs Java connaissent tous tres bien la notion de classe immuable. Beaucoup ont d'ailleurs peste contre sa principale representante, java.lang.String. L'expression classe immuables est en fait un abus de langage, un substitut courant pour objet immuable. Un objet dit immuable est une instance de classe dont les membres exportes (ou visibles, que cela soit par un modificateur d'access direct ou indirect, protected, public ou package private) ne peuvent etre modifies apres creation.

Les classes immuables ont de nombreux avantages en leur faveur et leur utilisation simplifie parfois tellement le developpement que je voulais ecrire ce billet pour vous encourager a en utiliser aussi souvent que possible. De part leur propriete intrinseque, ces objets ont un seul etat. Cette derniere remarque peut vous sembler anodine mais a de nombreuses repercussions tres importantes. Voici en vrac les avantages des objets immuables :

  • Ils sont garantis thread-safe
  • Ils peuvent etre mis en cache
  • Ils n'ont besoin ni de constructeur par copie, ni d'implementation de l'interface Cloneable
  • Ils n'est pas necessaire d'en faire une copie defensive
  • Leurs invariants sont testes a la creation seulement
  • Ils constituent d'excellentes cles pour les Map et Set
  • Leurs valeurs peuvent etre mises en cache par le client sans risque de desynchronisation

Les classes immuables sont particulierement adaptees a la representation de types de donnees abstraits. L'API de Java en contient plusieurs exemples : Integer, Color, BigDecimal, etc. La definition de type abstrait depend toutefois de votre application. Ainsi, un logiciel affichant le contenu d'un magasin en ligne (livres, musique, DVD, etc.) pourra utiliser des classes immuables pour representer les articles.

Ecrire une classe immuable n'est pas une tache difficile mais demande beaucoup d'attention pour ne pas exporter indirectement des valeurs. Voici les regles a suivre :

  • La classe doit etre declaree final (dans le cas contraire, il serait possible de modifier une instance par heritage)
  • Tous les champs doivent etre declares final
  • La reference a this ne doit jamais etre exportee
  • Tous les champs faisant reference a un objet non immuable doivent etre prives, ne jamais etre exportes, representer l'unique reference a cet objet et ne jamais modifier l'etat de l'objet

Le dernier point est le plus delicat mais evident avec un exemple :

private final Date theDate;

public MaClasse(Date theDate) {
  this.theDate = theDate;
}

@Override
public String toString() {
  return theDate.toString();
}

A premiere vue, cet exemple est correct puisque le membre theDate est prive, final et jamais exporte. En outre, la valeur renvoyee par toString est immuable. En realite, theDate est bel et bien exporte, indirectement :

Date d = new Date();
MaClasse c = new MaClasse(d);
d.setYear(98);
System.out.println(c);

Ce programme affiche Fri Mar 27 00:50:23 PST 1998, notre classe n'est donc pas immuable. Pour resoudre ce probleme, il faut realiser une copie defensive des objets non immuables passes en parametre :

private final Date theDate;

public MaClasse(Date theDate) {
  this.theDate = (Date) theDate.clone();
}

Cette fois-ci le resultat est bien Mon Mar 27 00:52:18 PST 2006 malgre la modification de l'annee. Ce qui est vrai pour les parametres d'entree de la classe l'est egalement pour les valeurs que la classe renvoie a travers ses differentes methodes. Ainsi, pour ajouter une methode getDate() a notre classe, nous devrons ecrire ceci :

public Date getDate() {
  return (Date) theDate.clone();
}

Malheureusement, la copie defensive ne suffit pas toujours. Prenez l'exemple suivant :

private final Date startDate;
private final Date endDate;

public MaClasse(Date startDate, Date endDate) {
  if (startDate.compareTo(endDate) > 0) {
    throw new IllegalArgumentException("The start date is not <= the end date.");
  }

  this.startDate = (Date) startDate.clone();
  this.endDate = (Date) endDate.clone();
}

Ce code semble parfait a premiere vue mais cache un gros probleme potentiel. En effet, il est possible avec un thread de modifier startDate et/ou endDate de maniere a ce que la condition soit validee mais que des valeurs interdites soient conservees par l'objet. Puisqu'il n'est pas possible de predire le sequencement des operations multi-thread dans la plupart des environnement, rien ne garantit que le thread principal ne s'arretera pas juste apres l'execution du if pour donner la main a un second thread qui modifiera startYear pour que sa valeur soit superieure a endYear. La bonne strategie (la seule en fait) est la suivante :

private final Date startDate;
private final Date endDate;

public MaClasse(Date startDate, Date endDate) {
  Date copyStart = (Date) startDate.clone();
  Date copyEnd = (Date) endDate.clone()

  if (copyStart.compareTo(copyEnd) > 0) {
    throw new IllegalArgumentException("The start date is not <= the end date.");
  }

  this.startDate = copyStart;
  this.endDate = copyEnd;
}

Ce dernier exemple montre bien pourquoi les classes immuables sont souvent interessantes. Si Date etait immuable, nous pourrions simplement ecrire this.startDate = startDate.

Rendez-vous service, utilisez des classes immuables :)



Cette news a été rédigée en écoutant Fall Out Boy - Sugar, We're Goin Down


 Romain GUY (Gfx)

 lundi 27 mars 2006 @ 14:58
  
Gfx je t'aimeeeeeeeeeeeeeeeeeeeeeeeeuh !
J'avais jamais trouvé d'explication potable justifiant le fait que String n'était pas modifiable après sa création.
(oui, je connaissais pas le terme d'objet immuable ;) )
Wizmaster 
Gravatar Image
 lundi 27 mars 2006 @ 20:01
  
Imagine juste le bordel avec les threads si les String etaient modifiables ;-)
Gfx 
Gravatar Image
 lundi 27 mars 2006 @ 21:03
  
je connaissais pas le terme d'objet immuable


Idem. Petit billet très intéressant. Je n'avais jamais vraiment "creusé" cette aspect.

@+
thev 
Gravatar Image
 mardi 04 avril 2006 @ 18:46
  
Super explication, très claire !
Je vais diffuser ce billet à mon entourage ...

++
Jérôme.
jerome 
Gravatar Image

 Ajoutez votre grain de sel 
 
Surnom :
E-mail :
Message :     B     I     U     CODE     QUOTE     IMAGE     CD CASE     LINK 
 
Un gâteau ?oui    non 
RisoliVillard ?oui    non 
Port de RisoliVillard :
     


 Aide
RisoliVillard est un plugin Winamp 2/5, iTunes et un outil pour XMMS qui vous permettra d'afficher la chanson que vous écoutez au moment de l'écriture de votre réponse. Le port utilisé par votre plugin doit être reproduit dans le champ ci-dessus (8462 par défaut).
Utilisation de vBCode :
- [B]gras[/B]
- [I]italique[/I]
- [U]souligné[/U]
- [QUOTE]citation[/QUOTE]
- [CODE]code[/CODE]
- [IMG]http://www.serveur.com/image.jpg[/IMG]
- [URL=http://www.serveur.com/]texte à afficher[/URL]

 
#ProgX©2005 Mathieu GINOD - Romain GUY - Erik LOUISE