web-dev-qa-db-fra.com

Java: solution recommandée pour le clonage / copie en profondeur d'une instance

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.

Fais le toi-même:

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)

Utilisez la réflexion:

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

Utiliser un framework de clonage:

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

Utiliser une instrumentation de code binaire pour écrire un clone au moment de l'exécution

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.

171
Guillaume

Pour le clonage en profondeur (clone toute la hiérarchie des objets):

  • 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.

Pour le clonage superficiel (clone uniquement les propriétés de premier niveau):

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é.

150
Bozho

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);)

35
LeWoody

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.

9
Andrey Chaschev

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));
5
Ranx

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);

}
2
tiboo

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.

2
Yoni Roit

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.

1
Dominik Sandjaja

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.

1
x4u