Rappelons que l'affectation d'un objet à une variable écrit dans cette
variable une référence à l'objet, mais ne crée pas un nouvel objet. Si
l'on veut dupliquer un objet, on peut recourir à la méthode
clone, définie dans la classe Object. Celle-ci
commence par tester si la classe de l'objet réalise l'interface
Cloneable (voir § ) ; si c'est le cas, elle
effectue une copie superficielle de l'objet, et sinon, elle déclenche
l'exception CloneNotSupportedException. Le
transtypage (A) est nécessaire, car
clone() retourne un Object, pas un A :
A a1 = new A(); A a2 = (A)a1.clone();
Cependant, comme la visibilité de clone est « protected », cette invocation ne peut figurer qu'à l'intérieur de la classe de l'objet à dupliquer ; sinon, le message d'erreur « clone() has protected access in java.lang.Object » est émis par le compilateur. Afin de pouvoir dupliquer un objet utilisé dans une classe quelconque, il faut redéfinir la méthode clone dans sa classe, en invoquant super.clone() et en la rendant publique :
class A implements Cloneable { // ... public Object clone() throws CloneNotSupportedException { return super.clone(); } }
Si l'on veut pouvoir invoquer clone sans être obligé de traiter CloneNotSupportedException, il suffit de récupérer cette exception dans la redéfinition de clone, par exemple de la façon suivante :
public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(e.toString()); }
Les types de tableaux héritent de toutes les méthodes publiques de la classe Object, tandis que la méthode clone est redéfinie comme une méthode publique (donc utilisable) qui ne déclenche pas l'exception CloneNotSupportedException ; elle réalise une copie d'un tableau :
int[] a = new int[] {1, 2, 3, 4}; int[] b = (int[]) a.clone();
Signalons cette méthode clone réalise une copie superficielle
d'un tableau (le tableau est copié, mais pas les objets auxquels
référent ses éléments, figure ) :
Point[] tp1 = (Point[]) tp.clone();
La méthode clone définie dans Object ou redéfinie pour les tableaux est utile quand les objets d'une structure de données sont exactement représentés par l'ensemble des valeurs de leurs champs : ceci est le cas si tous les champs sont d'un type primitif (par exemple, la classe des nombres complexes, qui a deux champs de type double), ou pour un tableau d'éléments de type primitif. Dans d'autres cas, par exemple pour des arbres binaires, la copie d'une structure ArbreBinaire n'entraine pas une copie de l'arbre complet, mais seulement de la racine :
ArbreBinaire a = new ACons(0, new ACons(1, AVide.val(), AVide.val()), new ACons(2, AVide.val(), AVide.val())); ArbreBinaire a1 = a; ArbreBinaire a2 = (ArbreBinaire)a.clone();
où le type ArbreBinaire est une interface, dont les deux classes d'implémentation AVide et ACons représentent respectivement l'arbre vide et les arbres non vides.
Alors que a1 et a désignent le même objet, la
définition de a2 crée un nouvel objet, mais les objets au
deuxième niveau de l'arbre sont identiques : il y a partage d'objets par
co-référencement (figure ). Une modification
d'un n
ud de a2 revient donc à une modification de
a et de a1, ce qui n'est pas toujours souhaité.
Il faut donc redéfinir la méthode clone pour
la classe ArbreBinaire afin qu'elle parcoure l'arbre et effectue des
copies de tous les objets, et pas seulement de celui qui figure à sa
racine (figure ). Un parcours en profondeur donne la
fonction suivante :
interface ArbreBinaire { // ... Object clone(); } class AVide implements ArbreBinaire { // ... public Object clone() { return this; } } class ACons implements ArbreBinaire { // ... public Object clone() { try { ACons a = (ACons) super.clone(); if (gauche != null) a.gauche = (ArbreBinaire)gauche.clone(); if (droit != null) a.droit = (ArbreBinaire)droit.clone(); return a; } catch (CloneNotSupportedException e) { throw new InternalError(e.toString()); } } }
En général, on souhaite que pour un objet , la valeur de x ==
x.clone() soit false. Le cas de la classe AVide est
une exception, puisqu'elle est définie afin de n'admettre qu'une seule
instance, l'arbre vide : un clone de l'arbre vide sera donc identique à
l'arbre vide.