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


Je me demande s'il existe un moyen recommandé de faire un clone/copie profond d'instance en java.

J'ai 3 solutions en tête, mais je peux en manquer, et j'aimerais avoir votre avis

Edit: include Bohzo propositon and refine question: il s'agit plus de clonage profond que de clonage superficiel.

Faites-le vous-même:

Coder le clone à la main après les propriétés et vérifier que les instances mutables sont également clonées.
pro:
- le contrôle de ce que sera effectué
- la rapidité d'exécution
inconvénients:
- fastidieux à écrire et à maintenir
- sujet aux bogues (échec de copier/coller, propriété manquante, propriété mutable réaffectée)

Utiliser la réflexion:

Avec vos propres outils de réflexion 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 en une seule ligne.
pro:
- facile à écrire
- pas d'entretien
inconvénients:
- moins de contrôle de ce qui se passe
- sujet aux bogues avec l'objet mutable si l'outil de réflexion ne clone pas aussi les sous-objets
- exécution plus lente

Utiliser clone cadre:

Utiliser un framework qui le font pour vous, comme :
commons-lang SerializationUtils
Java Profonde Clonage de la Bibliothèque
Dozer
Kryo

Pro:
- identique à la réflexion
- plus de contrôle sur ce qui sera exactement cloné.
inconvénients:
- chacun l'instance mutable est entièrement clonée, même à la fin de la hiérarchie
- pourrait être très lent à exécuter

Utiliser l'instrumentation bytecode pour écrire le clone à l'exécution

Javassit, BCEL ou cglib peut être utilisé pour générer un dédié cloner aussi rapide qu'une main écrits. Quelqu'un connaît une bibliothèque utilisant l'un de ces outils à cette fin ?

Ce que j'ai manqué ici ?
Lequel recommanderiez-vous ?

Merci.

Author: Guillaume, 2010-01-28

8 answers

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

  • Commons-lang SerializationUtils - utilisation de 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 implémenter Serializable, ou dans les cas où vous ne voulez 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 "do-it-yourself" - l'API ci-dessus fournit un bon contrôle sur ce qu'il faut et ce qu'il ne faut pas cloner (par exemple en utilisant transient, ou String[] ignoreProperties), donc réinventer la roue n'est pas préférable.

 147
Author: Bozho, 2016-04-28 18:30:00

Le livre de Joshua Bloch a un chapitre entier intitulé "Item 10: Override Clone Judicieusement" dans lequel il explique pourquoi le remplacement du clone pour la plupart est une mauvaise idée car la spécification Java crée de nombreux problèmes.

Il fournit quelques alternatives:

  • Utiliser un modèle d'usine à la place d'un constructeur:

         public static Yum newInstance(Yum yum);
    
  • Utiliser un constructeur de copie:

         public Yum(Yum yum);
    

Toutes les classes de collection en Java prennent en charge la copie constructeur (par exemple new ArrayList (l);)

 35
Author: LWoodyiii, 2010-01-28 22:29:28

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 pouvez trouver une liste des sociétés qui l'utilisent dans la production.

 7
Author: Andrey Chaschev, 2013-11-22 10:29:36

Utilisez XStream toXML/fromXML en mémoire. Extrêmement rapide et existe depuis longtemps et va fort. Les objets n'ont pas besoin d'être sérialisables et vous n'avez pas use reflection (bien que XStream le fasse). XStream peut discerner les variables qui pointent vers le même objet et ne pas accidentellement faire deux copies complètes de l'instance. Beaucoup de détails comme ça ont été martelés au fil des ans. Je l'ai utilisé pour un certain nombre d'années et c'est un aller. Il est aussi facile à utiliser que vous pouvez 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
Author: Ranx, 2015-07-28 03:18:15

Dépend.

Pour la vitesse, utilisez le BRICOLAGE. Pour l'épreuve des balles, utilisez la réflexion.

BTW, la sérialisation n'est pas la même que refl, car certains objets peuvent fournir des méthodes de sérialisation surchargées (readObject/writeObject) et ils peuvent être bogués

 2
Author: Yoni Roit, 2010-01-28 16:51:49

Je recommanderais la méthode DIY qui, combinée à une bonne méthode hashCode() et equals() devrait être facile à prouver dans un test unitaire.

 1
Author: Dominik Sandjaja, 2010-01-28 16:47:45

Je suggère de remplacer l'objet.clone (), appelez super.clone () d'abord et puis appelez ref = ref.clone () sur toutes les références que vous souhaitez avoir copiées en profondeur. C'est plus ou moins une approche Do it yourself mais nécessite un peu moins de codage.

 1
Author: x4u, 2010-01-28 16:48:32

Pour les objets compliqués et lorsque les performances ne sont pas significatives, j'utilise gson pour sérialiser l'objet json texte, 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 transient les champs ne seront pas copiés et les objets avec référence circulaire avec 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);

}
 1
Author: tiboo, 2016-11-29 07:59:14