Question

En Java, il est un peu difficile d'implémenter une fonction de copie d'objet en profondeur. Quelles mesures prenez-vous pour vous assurer que l'objet d'origine et l'objet cloné ne partagent aucune référence?

Était-ce utile?

La solution

Un moyen sûr consiste à sérialiser l'objet, puis à le désérialiser. Cela garantit que tout est une toute nouvelle référence.

Voici un article sur la façon de procéder efficacement .

Mises en garde: il est possible que les classes remplacent la sérialisation de sorte que les nouvelles instances ne soient pas créées, par exemple. pour les singletons. De plus, cela ne fonctionne évidemment pas si vos classes ne sont pas sérialisables.

Autres conseils

Quelques personnes ont mentionné l'utilisation ou la substitution de Object.clone () . Ne le fais pas. Object.clone () a quelques problèmes majeurs et son utilisation est déconseillée dans la plupart des cas. Reportez-vous à la rubrique 11 de " Java effectif " de Joshua Bloch pour une réponse complète. Je pense que vous pouvez utiliser Object.clone () en toute sécurité sur des tableaux de types primitifs, mais à part cela, vous devez faire preuve de discernement pour utiliser correctement et remplacer le clone.

Les schémas qui reposent sur la sérialisation (XML ou autre) sont kludgy.

Il n’ya pas de réponse facile ici. Si vous souhaitez copier en profondeur un objet, vous devez parcourir le graphe d'objet et copier explicitement chaque objet enfant via le constructeur de copie de l'objet ou une méthode de fabrique statique qui à son tour copie en profondeur l'objet enfant. Les immutables (par exemple, String ) ne doivent pas être copiés. En passant, vous devriez préférer l'immuabilité pour cette raison.

Vous pouvez effectuer une copie complète avec la sérialisation sans créer de fichiers.

Votre objet que vous souhaitez copier en profondeur devra mettre en œuvre une structure sérialisable . Si la classe n'est pas finale ou ne peut pas être modifiée, développez-la et implémentez-la sérialisable.

Convertissez votre classe en un flux d'octets:

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
oos.flush();
oos.close();
bos.close();
byte[] byteData = bos.toByteArray();

Restaurez votre classe à partir d'un flux d'octets:

ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
(Object) object = (Object) new ObjectInputStream(bais).readObject();

Vous pouvez créer un clone complet basé sur la sérialisation en utilisant org.apache.commons.lang3.SerializationUtils.clone (T) dans Apache Commons, mais faites attention & # 8212; la performance est catastrophique.

En général, il est recommandé d'écrire vos propres méthodes de clonage pour chaque classe d'un objet du graphe d'objets nécessitant un clonage.

Une façon d'implémenter la copie complète est d'ajouter des constructeurs de copie à chaque classe associée. Un constructeur de copie prend une instance de 'this' comme argument unique et en copie toutes les valeurs. Beaucoup de travail, mais assez simple et sûr.

EDIT: notez qu'il n'est pas nécessaire d'utiliser des méthodes d'accès pour lire les champs. Vous pouvez accéder directement à tous les champs car l'instance source est toujours du même type que l'instance avec le constructeur de copie. Évident mais peut être négligé.

Exemple:

public class Order {

    private long number;

    public Order() {
    }

    /**
     * Copy constructor
     */
    public Order(Order source) {
        number = source.number;
    }
}


public class Customer {

    private String name;
    private List<Order> orders = new ArrayList<Order>();

    public Customer() {
    }

    /**
     * Copy constructor
     */
    public Customer(Customer source) {
        name = source.name;
        for (Order sourceOrder : source.orders) {
            orders.add(new Order(sourceOrder));
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Modifier: Notez que lorsque vous utilisez des constructeurs de copie, vous devez connaître le type d’exécution de l’objet que vous copiez. Avec l'approche ci-dessus, vous ne pouvez pas facilement copier une liste mélangée (vous pourrez peut-être le faire avec un code de réflexion).

Apache commons offre un moyen rapide de cloner en profondeur un objet.

My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);

Vous pouvez utiliser une bibliothèque dotée d'une API simple et permettant un clonage relativement rapide avec réflexion (devrait être plus rapide que les méthodes de sérialisation).

Cloner cloner = new Cloner();

MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o

XStream est vraiment utile dans de tels cas. Voici un code simple pour faire du clonage

private static final XStream XSTREAM = new XStream();
...

Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));

Une approche très simple consiste à utiliser Jackson JSON pour sérialiser un objet Java complexe en JSON et le relire.

http://wiki.fasterxml.com/JacksonInFiveMinutes

Utilisez XStream ( http://x-stream.github.io/ ). Vous pouvez même contrôler les propriétés que vous pouvez ignorer par le biais d'annotations ou en spécifiant explicitement le nom de la propriété dans la classe XStream. De plus, vous n'avez pas besoin d'implémenter une interface clonable.

Pour les utilisateurs Spring Framework . Utilisation de la classe org.springframework.util.SerializationUtils :

@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
     return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}

La copie en profondeur ne peut être effectuée qu'avec le consentement de chaque classe. Si vous avez le contrôle sur la hiérarchie des classes, vous pouvez implémenter l'interface clonable et la méthode Clone. Sinon, il est impossible d'effectuer une copie en profondeur en toute sécurité, car l'objet peut également partager des ressources autres que des données (connexions à une base de données, par exemple). En général, toutefois, la copie en profondeur est considérée comme une mauvaise pratique dans l'environnement Java et doit être évitée via les pratiques de conception appropriées.

import com.thoughtworks.xstream.XStream;

public class deepCopy {
    private static  XStream xstream = new XStream();

    //serialize with Xstream them deserialize ...
    public static Object deepCopy(Object obj){
        return xstream.fromXML(xstream.toXML(obj));
    }
}

Pour les objets compliqués et lorsque les performances ne sont pas significatives, j'utilise une bibliothèque json, telle que gson . sérialiser l'objet en texte json, puis désérialiser le texte pour obtenir un nouvel objet.

gson qui, sur la base de la réflexion, fonctionnera dans la plupart des cas, sauf que les champs transitoires ne seront pas copiés et les objets à référence circulaire de cause StackOverflowError .

public static <T> T copy(T anObject, Class<T> classInfo) {
    Gson gson = new GsonBuilder().create();
    String text = gson.toJson(anObject);
    T newObject = gson.fromJson(text, classInfo);
    return newObject;
}
public static void main(String[] args) {
    String originalObject = "hello";
    String copiedObject = copy(originalObject, String.class);
}

J'ai utilisé Bulldozer pour cloner des objets java et c'est génial, La bibliothèque Kryo est une autre excellente alternative.

1)

public static Object deepClone(Object object) {
   try {
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     ObjectOutputStream oos = new ObjectOutputStream(baos);
     oos.writeObject(object);
     ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
     ObjectInputStream ois = new ObjectInputStream(bais);
     return ois.readObject();
   }
   catch (Exception e) {
     e.printStackTrace();
     return null;
   }
 }

2)

    // (1) create a MyPerson object named Al
    MyAddress address = new MyAddress("Vishrantwadi ", "Pune", "India");
    MyPerson al = new MyPerson("Al", "Arun", address);

    // (2) make a deep clone of Al
    MyPerson neighbor = (MyPerson)deepClone(al);

Ici, vos classes MyPerson et MyAddress doivent implémenter une interface serilazable

BeanUtils fait un très bon travail de clonage en profondeur des beans.

BeanUtils.cloneBean(obj);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top