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 5.2). Une modification d'un n3#3ud 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 afin qu'elle parcoure l'arbre et effectue des copies de tous les objets, et pas seulement de celui qui figure à sa racine (figure 5.2) : c'est un clonage profond. L'interface ArbreBinaire, en tant que sous-type d'Object, hérite des déclarations de toutes méthodes publiques d'Object, mais pas de clone, qui est protégée : il faut donc déclarer clone dans l'interface :
interface ArbreBinaire { // ... Object clone(); }
En général, on souhaite que pour un objet 70#70, la valeur de x == x.clone() soit false. Le cas de la classe AVide est une exception à cette règle, 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 :
class AVide implements ArbreBinaire { // ... public Object clone() { return this; } }
Il n'est pas nécessaire que cette classe réalise l'interface Cloneable, car la méthode clone définie dans Object n'est pas invoquée sur son instance. Par contre, la classe ACons doit réaliser Cloneable. Un parcours en profondeur est réalisé de la façon suivante :
class ACons implements ArbreBinaire, Cloneable { // ... public Object clone() { try { ACons a = (ACons) super.clone(); a.gauche = (ArbreBinaire) gauche.clone(); a.droit = (ArbreBinaire) droit.clone(); return a; } catch (CloneNotSupportedException e) {} } }