Vous aimez ce que vous lisez sur ce blog ?
Envie d'aller plus loin avec véritable formation d'expertise en Java ?
Venez suivre ma formation Masterclasse Expertise Java !

"Même un développeur experimenté a besoin de continuer à apprendre. Et dans cette formation... j'ai appris beaucoup !" - A.G., Java Champion

Sessions intra-entreprises sur demande : contact[at]mokatech.net.
Inscrivez-vous vite !

Java Quiz #10

En C++, pour dupliquer un objet, on utilise habituellement le constructeur par recopie. C'est un constructeur qui n'attend qu'un argument, de même type que le type de celui que l'on construit (voir par exemple le constructeur String(String)).

En Java, Sun recommande d'implémenter l'interface Cloneable et d'utiliser la méthode clone(), plutôt qu'un constructeur par recopie, pour dupliquer un objet.

Pourquoi ?

Réponse : Quand on considère une classe tout seule, les 2 syntaxes se valent. Le problème se pose lorsqu'on définit une hiérarchie de classes. Par exemple :

  1. class Employe { ... }
  2. class Manager extends Employe { ... }
  3. class Prestataire extends Employe { ... }
  4.  
  5. ...
  6.  
  7. Collection lesEmployesDeMonBatiment = ...;

Ici la collection contient des objets, qui sont soit des employés, soit des managers, soit des prestataires (oui, on s'interdit les managers prestataires ! ;-) ). Comment dupliquer le contenu de la liste ?

Avec le constructeur par recopie, il faut écrire :

  1. Collection collectionCopiee = ...; // Cree une nouvelle collection vide
  2. for (Employe employe : lesEmployesDeMonBatiment) {
  3. Employe employeCopie;
  4.  
  5. if (employe instanceof Manager) {
  6. employeCopie = new Manager((Manager) employe);
  7. } else if (employe instanceof Prestataire) {
  8. employeCopie = new Prestataire((Prestataire) employe);
  9. } else
  10. employeCopie = new Employe(employe);
  11. }
  12. collectionCopiee.add(employeCopie);
  13. }

Et à chaque fois qu'un nouveau sous-type de Employe est créé, il faut modifier ce bloc de code, et ajouter un autre "if (employe instanceof ...) {" sur ce nouveau sous-type (quelle magnifique évolutivité du code !)

En Java, avec l'interface Cloneable (que doit implémenter Employe), il suffit d'écrire (et ce, quel que soit le nombre de sous-classes d'Employe) :

  1. Collection collectionCopiee = ...; // Cree une nouvelle collection vide
  2.  
  3. for (Employe employe : lesEmployesDeMonBatiment) {
  4. collectionCopiee.add(employe.clone());
  5. }

Plus simple, non ?

Question subsidiaire : pourquoi, en Java, faut-il explicitement implémenter l'interface Cloneable pour qu'un objet puisse être cloné, alors que la méthode clone() est définie dans la classe Object (donc pour tout objet) ?

Réponse : parce que le clonage n'a pas toujours de sens pour un objet. Car autant cloner une collection a du sens, autant cloner l'unique instance de Runtime (qui représente la machine virtuelle) n'en a pas. Forcer à implémenter l'interface Cloneable permet donc de contrôler finement ce qui peut être cloné de ce qui ne doit pas l'être.


Commentaires

1. Le mercredi 14 mai 2008, 11:51 par Histrion

Basiquement en C++ ce que j'ai vu faire (en mission, en C++) c'est un memcpy : on copie bit à bit le contenu de la zone mémoire qui contient l'objet source, vers une zone mémoire qui contiendra l'objet cible (zone qu'on a alloué de la taille de l'objet avec un malloc). Bilan on a un pointeur sur le nouvel objet. La conséquence c'est en particulier que les membres pointeurs sont copiés à l'identique : l'objet cible pointe sur les même zones mémoire que l'objet source.

Exemple :
ObjDst.pMembre vaudra après "memcpy" la même chose que ObjSrc.pMembre, et l'accès ObjDst->pMembre est une violation du principe d'encapsulation.

Si on veut faire propre il faut donc non seulement faire un memcpy mais également ensuite des new/malloc/mise à null pour tous les pointeurs membres. Ce petit témoignage pour souligner un des nombreux écueils que représentent les pointeurs : avec eux il faut toujours faire très attention à ce qu'on code.

Du coup un élément de réponse : et si en fait on voulait vraiment avoir des références plutôt que de nouveaux membres (dans mon exemple : et si on voulait vraiment que pMembre soit le même dans les deux classes ?). Je pense que c'est pour ça que clone() est là en Java : do what you really want.

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.