La copie d'objets en Java est le processus de création d'un nouvel objet ayant le même état qu'une instance existante. Historiquement, la copie est devenue pertinente dès le début du développement de Java, lorsqu'il est devenu nécessaire de dupliquer des objets sans les recréer manuellement. Java lui-même ne fournit pas de moyen universel de copier des objets, utilisant plutôt le contrat clone() et le modèle du constructeur de copie.
La méthode clone() a été introduite dans l'interface Cloneable pour fournir un mécanisme standard de clonage d'objets, cependant, son implémentation présente de nombreuses nuances et entraîne souvent des bugs.
Le principal problème est l'absence de véritable copie profonde "prête à l'emploi" et la possibilité d'un comportement imprévisible lors du clonage superficiel (shallow copy). Le constructeur de copie permet d'implémenter à la fois une copie superficielle et une copie profonde, mais nécessite une implémentation explicite.
Pour un clonage sécurisé et correct d'objets complexes, il est préférable d'utiliser un constructeur de copie ou une méthode de fabrique, tandis que clone() doit être utilisé avec précaution — seulement si l'objet s'adapte véritablement à ce contrat.
Exemple de code :
// Clonage superficiel via clone() public class Address implements Cloneable { String city; public Address(String city) { this.city = city; } @Override public Address clone() throws CloneNotSupportedException { return (Address) super.clone(); } } public class Person implements Cloneable { String name; Address address; public Person(String name, Address address) { this.name = name; this.address = address; } @Override public Person clone() throws CloneNotSupportedException { Person copy = (Person) super.clone(); copy.address = address.clone(); // copie profonde de address return copy; } } // Constructeur de copie public class Person { String name; Address address; public Person(Person other) { this.name = other.name; this.address = new Address(other.address.city); } }
Caractéristiques clés :
clone() nécessite l'implémentation de l'interface Cloneable et la redéfinition de la méthode clone.Question 1 : Que se passe-t-il si la classe n'implémente pas Cloneable, que se passe-t-il lors de l'appel de clone() ?
clone() lancera CloneNotSupportedException, si l'objet n'implémente pas l'interface Cloneable.
Question 2 : Le clonage via clone() est-il profond par défaut ?
Non, par défaut, seule la copie superficielle est mise en œuvre, toutes les références aux objets sont copiées telles quelles.
Question 3 : Peut-on se passer du constructeur de copie pour un clonage profond ?
Non, si vous souhaitez contrôler le processus, vous devez explicitement coder la copie des objets imbriqués via le constructeur ou manuellement dans clone().
Un développeur a appliqué le clone() standard dans la classe Order avec un champ List<Product>. En conséquence, lorsque la liste des produits a été modifiée dans la copie, les produits de l'original ont également changé, ce qui a causé un bug.
Avantages :
Inconvénients :
Dans la société, un constructeur de copie a été implémenté pour toutes les entités de domaine, et le processus de clonage pour des objets imbriqués complexes a été couvert par des tests.
Avantages :
Inconvénients :