Je me demande s'il existe une méthode recommandée pour effectuer une copie/copie en profondeur d'une instance en Java.
J'ai 3 solutions en tête, mais certaines peuvent me manquer et j'aimerais avoir votre avis.
modifier: inclure la proposition Bohzo et affiner la question: il s’agit plus du clonage en profondeur que du clonage superficiel.
codez manuellement les propriétés du clone après les propriétés et vérifiez que les instances mutables sont également clonées.
pro:
- contrôle de ce qui sera exécuté
- exécution rapide
contre:
- fastidieux à écrire et à maintenir
- sujet à un bogue (échec de copier/coller, propriété manquante, propriété mutable réaffectée)
Avec vos propres outils de réflexion ou avec une aide externe (telle que jakarta common-beans), il est facile d'écrire une méthode de copie générique qui fera le travail en une seule ligne.
pro:
- facile à écrire
- pas d'entretien
contre:
- moins de contrôle de ce qui se passe
- bogue sujet à un objet mutable si l'outil de réflexion ne clone pas trop les sous-objets
- exécution plus lente
Utilisez un cadre qui le fait pour vous, comme:
commons-lang SerializationUtils
Bibliothèque Java Deep Cloning
bulldozer
Kryo
pro:
- identique à la réflexion
- plus de contrôle sur ce qui sera exactement cloné.
contre:
- 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 peut être utilisé pour générer un cloneur dédié aussi vite qu'une main écrite. Quelqu'un connaît une bibliothèque utilisant l'un de ces outils à cette fin?
Qu'est-ce que j'ai manqué 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 - utilisation de la réflexion - dans les cas où les classes ou les objets que vous souhaitez cloner sont hors de votre contrôle (une bibliothèque tierce) et que vous ne pouvez pas les forcer à implémenter Serializable
ou dans les cas où vous ne souhaitez pas implémenter Serializable
.
commons-beanutils BeanUtils - dans la plupart des cas.
Spring BeanUtils - si vous utilisez déjà spring et avez donc cet utilitaire sur le classpath.
J'ai délibérément omis l'option "à faire soi-même" - les API ci-dessus fournissent un bon contrôle sur ce qu'il faut ou ne pas cloner (par exemple, en utilisant transient
ou String[] ignoreProperties
), réinventant ainsi la roue n'est pas préféré.
Le livre de Joshua Bloch contient un chapitre entier intitulé "Point 10: Remplacer le clone de manière judicieuse" dans lequel il explique pourquoi le clonage de substitution est généralement une mauvaise idée, car la spécification de Java cela crée beaucoup de problèmes.
Il propose quelques alternatives:
Utilisez un modèle 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 dans 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, à la et de leur page, vous trouverez une liste des entreprises qui l'utilisent en production.
Utilisez XStream toXML/fromXML en mémoire. Extrêmement rapide et a été autour depuis longtemps et va fort. Les objets n'ont pas besoin d'être sérialisables et vous n'avez pas de réflexion par l'utilisation (bien que XStream en ait). XStream peut discerner les variables qui pointent sur le même objet et ne pas créer accidentellement deux copies complètes de l'instance. Beaucoup de détails comme ceux-là ont été élaborés au fil des ans. Je l'ai utilisé pendant un certain nombre d'années et c'est un aller à. Il est 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));
Pour les objets compliqués et lorsque les performances ne sont pas significatives, j'utilise gson pour sérialiser l'objet en texte json, puis désérialiser le texte pour obtenir un nouvel objet.
gson qui basé sur la réflexion fonctionnera dans la plupart des cas, sauf que les champs transient
ne seront pas copiés et les objets avec une référence circulaire avec la cause StackOverflowError
.
public static <ObjectType> ObjectType Copy(ObjectType AnObject, Class<ObjectType> ClassInfo)
{
Gson gson = new GsonBuilder().create();
String text = gson.toJson(AnObject);
ObjectType newObject = gson.fromJson(text, ClassInfo);
return newObject;
}
public static void main(String[] args)
{
MyObject anObject ...
MyObject copyObject = Copy(o, MyObject.class);
}
Dépend.
Pour la vitesse, utilisez le bricolage. Pour les balles, utilisez la réflexion.
BTW, la sérialisation n’est pas la même chose que refl, car certains objets peuvent fournir des méthodes de sérialisation surchargées (readObject/writeObject) et qu’elles peuvent être boguées.
Je recommanderais la méthode de bricolage qui, combinée à une bonne méthode hashCode () et equals (), devrait être facile à vérifier dans un test unitaire.
Je suggérerais de remplacer Object.clone (), appelez d'abord super.clone () et ensuite appelez ref = ref.clone () pour toutes les références que vous souhaitez copier en profondeur. C'est plus ou moins faites-le vous-même approche mais nécessite un peu moins de codage.