Je me demande s’il existe une méthode recommandée pour faire un clone profond / une copie d’instance en Java.
J’ai 3 solutions en tête, mais je peux en manquer et j’aimerais avoir votre avis
edit: incluez la proposition de Bohzo et affinez la question: il s’agit plus du clonage profond que du clonage superficiel.
coder manuellement les propriétés du clone après les propriétés et vérifier que les instances mutables sont également clonées.
pro:
– contrôle de ce qui sera effectué
– exécution rapide
les inconvénients:
– fastidieux d’écrire et de maintenir
– Problème de bogue (échec de copier / coller, propriété manquante, propriété mutable réassignée)
Avec vos propres outils de reflection ou avec un assistant externe (comme Jakarta Common-Beans), il est facile d’écrire une méthode de copie générique qui fera le travail sur une seule ligne.
pro:
– facile à écrire
– pas d’entretien
les inconvénients:
– moins de contrôle de ce qui se passe
– un bug avec un object mutable si l’outil de reflection ne clone pas aussi les sous-objects
– exécution plus lente
Utilisez un framework qui le fait pour vous, comme:
commons-lang SerializationUtils
Java Deep Cloning Library
Dozer
Kryo
pro:
– comme reflection
– plus de contrôle sur ce qui sera exactement cloné.
les inconvénients:
– chaque instance mutable est entièrement clonée, même à la fin de la hiérarchie
– pourrait être très lent à exécuter
javassit , BCEL ou cglib peuvent être utilisés pour générer un cloner dédié aussi rapidement qu’une main écrite. Quelqu’un connaît une lib utilisant l’un de ces outils à cet effet?
Qu’est-ce que j’ai raté ici?
Lequel recommanderiez-vous?
Merci.
commons-lang SerializationUtils – en utilisant la sérialisation – si toutes les classes sont sous votre contrôle et que vous pouvez forcer l’implémentation de Serializable
.
Java Deep Cloning Library – en utilisant la reflection – dans les cas où les classes ou les objects que vous souhaitez cloner sont hors de votre contrôle (une bibliothèque tierce) et que vous ne pouvez pas les implémenter, ou dans les cas où vous ne voulez pas implémenter Serializable
.
commons-beanutils BeanUtils – dans la plupart des cas.
Spring BeanUtils – si vous utilisez déjà Spring et que vous avez donc cet utilitaire sur le chemin de classe .
J’ai délibérément omis l’option “do-it-yourself” – les API ci-dessus permettent de bien contrôler les éléments à cloner (par exemple, en utilisant transient
ou Ssortingng[] ignoreProperties
). Il est donc préférable de réinventer la roue.
Le livre de Joshua Bloch a un chapitre entier intitulé “Item 10: Override Clone Judicieusement” dans lequel il explique pourquoi la plupart du temps, cloner le clone est une mauvaise idée car les spécifications de Java posent beaucoup de problèmes.
Il propose quelques alternatives:
Utilisez un motif d’usine à la place d’un constructeur:
public static Yum newInstance(Yum yum);
Utilisez un constructeur de copie:
public Yum(Yum yum);
Toutes les classes de collection de Java prennent en charge le constructeur de copie (par exemple, new ArrayList (l);)
Depuis la version 2.07, Kryo prend en charge le clonage superficiel / profond :
Kryo kryo = new Kryo(); SomeClass someObject = ... SomeClass copy1 = kryo.copy(someObject); SomeClass copy2 = kryo.copyShallow(someObject);
Kryo est rapide, et sur leur page vous pouvez trouver une liste des entresockets qui l’utilisent en production.
Utilisez XStream toXML / fromXML en mémoire. Extrêmement rapide et qui existe depuis longtemps et qui se porte bien. Les objects n’ont pas besoin d’être sérialisables et vous n’avez pas besoin de reflection (bien que XStream le fasse). XStream peut distinguer les variables qui pointent vers le même object et ne pas créer accidentellement deux copies complètes de l’instance. De nombreux détails comme ceux-là ont été élaborés au fil des ans. Je l’utilise depuis plusieurs années et c’est un rendez-vous. C’est à peu près aussi facile à utiliser que vous pouvez l’imaginer.
new XStream().toXML(myObj)
ou
new XStream().fromXML(myXML)
Cloner,
new XStream().fromXML(new XStream().toXML(myObj))
Plus succinctement:
XStream x = new XStream(); Object myClone = x.fromXML(x.toXML(myObj));
Dépend.
Pour la vitesse, utilisez le bricolage. Pour une résistance à toute épreuve, utilisez la reflection.
BTW, la sérialisation n’est pas la même chose que refl, car certains objects peuvent fournir des méthodes de sérialisation remplacées (readObject / writeObject) et ils peuvent être bogués
Je recommande le bricolage qui, combiné à une bonne méthode hashCode () et equals (), devrait être facile à tester dans un test unitaire.
Je suggère de remplacer Object.clone (), d’appeler super.clone () en premier et d’appeler ref = ref.clone () sur toutes les références que vous souhaitez avoir copiées en profondeur. C’est plus ou moins faire le faire soi-même mais nécessite un peu moins de codage.
Pour les objects compliqués et lorsque les performances ne sont pas significatives, j’utilise gson pour sérialiser l’object en texte json, puis désérialiser le texte pour obtenir un nouvel object.
gson qui, sur la base de la reflection, fonctionnera dans la plupart des cas, sauf que transient
champs transient
ne seront pas copiés et les objects avec référence circulaire avec la cause StackOverflowError
.
public static ObjectType Copy(ObjectType AnObject, Class ClassInfo) { Gson gson = new GsonBuilder().create(); Ssortingng text = gson.toJson(AnObject); ObjectType newObject = gson.fromJson(text, ClassInfo); return newObject; } public static void main(Ssortingng[] args) { MyObject anObject ... MyObject copyObject = Copy(o, MyObject.class); }