web-dev-qa-db-fra.com

Comment copier un objet en Java?

Considérons le code ci-dessous:

DummyBean dum = new DummyBean();
dum.setDummy("foo");
System.out.println(dum.getDummy()); // prints 'foo'

DummyBean dumtwo = dum;
System.out.println(dumtwo.getDummy()); // prints 'foo'

dum.setDummy("bar");
System.out.println(dumtwo.getDummy()); // prints 'bar' but it should print 'foo'

Donc, je veux copier la dum dans dumtwo et changer dum sans affecter la dumtwo. Mais le code ci-dessus ne fait pas cela. Lorsque je change quelque chose dans dum, le même changement se produit également dans dumtwo.

Je suppose que, lorsque je dis dumtwo = dum, Java copie le référence uniquement. Alors, est-il possible de créer une nouvelle copie de dum et de l’affecter à dumtwo?

699
Veera

Créez un constructeur de copie:

class DummyBean {
  private String dummy;

  public DummyBean(DummyBean another) {
    this.dummy = another.dummy; // you can access  
  }
}

Chaque objet possède également une méthode de clonage qui peut être utilisée pour copier l'objet, mais ne l'utilisez pas. C'est trop facile de créer une classe et d'utiliser une méthode de clonage inappropriée. Si vous voulez faire cela, lisez au moins ce que Joshua Bloch en dit dans Effective Java.

560
egaga

Basic: Copie d'objet en Java.

Supposons un objet - obj1, qui contient deux objets, contientObj1 et contientObj2
enter image description here

copie superficielle: _
une copie superficielle crée une nouvelle variable instance de la même classe, copie tous les champs dans la nouvelle instance et la renvoie. Object class fournit une méthode clone et prend en charge la copie superficielle.
enter image description here

Copie en profondeur:
Une copie profonde se produit lorsque un objet est copié avec les objets auxquels il fait référence. L'image ci-dessous montre obj1 après qu'une copie en profondeur a été effectuée. Non seulement obj1 a été copié, mais les objets qu'il contient ont également été copiés. Nous pouvons utiliser Java Object Serialization pour faire une copie en profondeur. Malheureusement, cette approche pose également certains problèmes ( exemples détaillés ). 
enter image description here

Problèmes possibles:
clone est délicat à mettre en œuvre correctement.
Il vaut mieux utiliser Copie défensive , constructeurs de copie (comme @egaga reply) ou méthodes d'usine statiques .

  1. Si vous avez un objet, que vous savez avoir une méthode publique clone(), mais que vous ne connaissez pas le type de l’objet au moment de la compilation, vous avez un problème. Java a une interface appelée Cloneable. En pratique, nous devrions implémenter cette interface si nous voulons créer un objet Cloneable. Object.clone est protégé, nous devons donc remplacer par une méthode publique pour qu'elle soit accessible.
  2. Un autre problème se pose lorsque nous essayons copie en profondeur d'un objet complexe . Supposons que la méthode clone() de toutes les variables d'objet membre effectue également une copie en profondeur, ce qui est trop risqué pour une hypothèse. Vous devez contrôler le code dans toutes les classes.

Par exemple, org.Apache.commons.lang.SerializationUtils aura une méthode pour le clone Deep utilisant la sérialisation ( Source ). Si nous avons besoin de cloner Bean, il existe plusieurs méthodes d’utilité dans org.Apache.commons.beanutils ( Source ).

  • cloneBean clonera un bean en fonction des getters et setters de propriétés disponibles, même si la classe de bean elle-même n'implémente pas Cloneable.
  • copyProperties Copiera les valeurs de propriété du bean Origin vers le bean de destination pour tous les cas où les noms de propriété sont identiques.
374
Chandra Sekhar

Le paquetage import org.Apache.commons.lang.SerializationUtils; contient une méthode:

SerializationUtils.clone(Object);

Exemple:

this.myObjectCloned = SerializationUtils.clone(this.object);
105
pacheco

Suivez juste comme ci-dessous:

public class Deletable implements Cloneable{

    private String str;
    public Deletable(){
    }
    public void setStr(String str){
        this.str = str;
    }
    public void display(){
        System.out.println("The String is "+str);
    }
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

et partout où vous voulez obtenir un autre objet, effectuez simplement un clonage . Par exemple: 

Deletable del = new Deletable();
Deletable delTemp = (Deletable ) del.clone(); // this line will return you an independent
                                 // object, the changes made to this object will
                                 // not be reflected to other object
96
Bhasker Tiwari

Pourquoi n'y a-t-il pas de réponse à utiliser l'API de Reflection?

private static Object cloneObject(Object obj){
        try{
            Object clone = obj.getClass().newInstance();
            for (Field field : obj.getClass().getDeclaredFields()) {
                field.setAccessible(true);
                field.set(clone, field.get(obj));
            }
            return clone;
        }catch(Exception e){
            return null;
        }
    }

C'est vraiment simple.

EDIT: inclure un objet enfant via la récursivité

private static Object cloneObject(Object obj){
        try{
            Object clone = obj.getClass().newInstance();
            for (Field field : obj.getClass().getDeclaredFields()) {
                field.setAccessible(true);
                if(field.get(obj) == null || Modifier.isFinal(field.getModifiers())){
                    continue;
                }
                if(field.getType().isPrimitive() || field.getType().equals(String.class)
                        || field.getType().getSuperclass().equals(Number.class)
                        || field.getType().equals(Boolean.class)){
                    field.set(clone, field.get(obj));
                }else{
                    Object childObj = field.get(obj);
                    if(childObj == obj){
                        field.set(clone, clone);
                    }else{
                        field.set(clone, cloneObject(field.get(obj)));
                    }
                }
            }
            return clone;
        }catch(Exception e){
            return null;
        }
    }
37
WillingLearner

J'utilise la bibliothèque JSON de Google pour le sérialiser, puis créer une nouvelle instance de l'objet sérialisé. Il copie en profondeur avec quelques restrictions:

  • il ne peut y avoir de références récursives

  • il ne copie pas les tableaux de types disparates

  • les tableaux et les listes doivent être tapés ou il ne trouvera pas la classe pour instancier

  • vous devrez peut-être encapsuler des chaînes dans une classe que vous déclarez vous-même

J'utilise également cette classe pour enregistrer les préférences de l'utilisateur, les fenêtres et tout ce qui ne doit pas être rechargé à l'exécution. Il est très facile à utiliser et efficace.

import com.google.gson.*;

public class SerialUtils {

//___________________________________________________________________________________

public static String serializeObject(Object o) {
    Gson gson = new Gson();
    String serializedObject = gson.toJson(o);
    return serializedObject;
}
//___________________________________________________________________________________

public static Object unserializeObject(String s, Object o){
    Gson gson = new Gson();
    Object object = gson.fromJson(s, o.getClass());
    return object;
}
       //___________________________________________________________________________________
public static Object cloneObject(Object o){
    String s = serializeObject(o);
    Object object = unserializeObject(s,o);
    return object;
}
}
25
Peter

Oui, vous faites simplement référence à l'objet. Vous pouvez cloner l'objet s'il implémente Cloneable.

Consultez cet article du wiki sur la copie d'objets.

Reportez-vous ici: Copie d'objet

22
Chrisb

Oui. Vous devez Deep Copy votre objet.

13
bruno conde

Ajoutez le code Cloneable et ci-dessous à votre classe

public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

Utilisez ce clonedObject = (YourClass) yourClassObject.clone();

12
Teja Maridu

Voici une explication décente de clone() si vous en avez besoin ...

Ici: clone (méthode Java)

10
Jon Bringhurst

Cela fonctionne aussi. En supposant modèle

class UserAccount{
   public int id;
   public String name;
}

Commencez par ajouter compile 'com.google.code.gson:gson:2.8.1' à votre application> gradez et synchronisez. ensuite

Gson gson = new Gson();
updateUser = gson.fromJson(gson.toJson(mUser),UserAccount.class);

Vous pouvez exclure l'utilisation d'un champ en utilisant le mot clé transient après le modificateur d'accès.

Note: C'est une mauvaise pratique. Aussi, ne recommandez pas d'utiliser Cloneable ou JavaSerialization C'est lent et cassé. Ecrire un constructeur de copie pour de meilleures performances ref .

Quelque chose comme

class UserAccount{
        public int id;
        public String name;
        //empty constructor
        public UserAccount(){}
        //parameterize constructor
        public UserAccount(int id, String name) {
            this.id = id;
            this.name = name;
        }

        //copy constructor
        public UserAccount(UserAccount in){
            this(in.id,in.name);
        }
    }

Statistiques de test de l'itération 90000:
Line UserAccount clone = gson.fromJson(gson.toJson(aO), UserAccount.class); prend 808ms

La ligne UserAccount clone = new UserAccount(aO); prend moins de 1ms

Conclusion: Utilisez gson si votre patron est fou et que vous préférez la vitesse. Utilisez le constructeur de deuxième copie si vous préférez la qualité.

Vous pouvez également utiliser le code constructeur de copie générateur plugin dans Android Studio.

8
Qamar

Deep Cloning est votre réponse, qui nécessite l'implémentation de l'interface Cloneable et le remplacement de la méthode clone().

public class DummyBean implements Cloneable {

   private String dummy;

   public void setDummy(String dummy) {
      this.dummy = dummy;
   }

   public String getDummy() {
      return dummy;
   }

   @Override
   public Object clone() throws CloneNotSupportedException {
      DummyBean cloned = (DummyBean)super.clone();
      cloned.setDummy(cloned.getDummy());
      // the above is applicable in case of primitive member types, 
      // however, in case of non primitive types
      // cloned.setNonPrimitiveType(cloned.getNonPrimitiveType().clone());
      return cloned;
   }
}

Vous l'appellerez comme ceci DummyBean dumtwo = dum.clone();

7
abbas

Pour ce faire, vous devez cloner l'objet d'une certaine manière. Bien que Java dispose d'un mécanisme de clonage, ne l'utilisez pas si vous n'y êtes pas obligé. Créez une méthode de copie qui effectue la copie pour vous, puis procédez comme suit:

dumtwo = dum.copy();

Ici est un peu plus de conseils sur différentes techniques pour réaliser une copie.

7
Yishai

Utilisez un utilitaire de clonage profond: 

SomeObjectType copy = new Cloner().deepClone(someObject);

Ceci permet de copier en profondeur tout objet Java. Consultez-le à l’adresse https://github.com/kostaskougios/cloning

7
Cojones

Autre que la copie explicite, une autre approche consiste à rendre l'objet immuable (pas de méthode set ni d'autres méthodes de mutation). De cette façon, la question ne se pose jamais. L’immuabilité devient plus difficile avec des objets plus grands, mais l’autre aspect est qu’elle vous pousse dans la direction de la scission en petits objets et composites cohérents.

5
class DB {
  private String dummy;

  public DB(DB one) {
    this.dummy = one.dummy; 
  }
}
3
Mahdi Abdi

Passez l'objet que vous souhaitez copier et récupérez-le:

private Object copyObject(Object objSource) {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(objSource);
            oos.flush();
            oos.close();
            bos.close();
            byte[] byteData = bos.toByteArray();
            ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
            try {
                objDest = new ObjectInputStream(bais).readObject();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return objDest;

    }

Maintenant, analysez la objDest à l'objet désiré.

Bon codage!

2
A-Droid Tech

Vous pouvez copier en profondeur automatiquement avec XStream, depuis http://x-stream.github.io/ :

XStream est une simple bibliothèque permettant de sérialiser des objets en XML et inversement encore.

Ajoutez-le à votre projet (si vous utilisez maven)

<dependency>
    <groupId>com.thoughtworks.xstream</groupId>
    <artifactId>xstream</artifactId>
    <version>1.3.1</version>                
</dependency>

Ensuite

DummyBean dum = new DummyBean();
dum.setDummy("foo");
DummyBean dumCopy = (DummyBean) XSTREAM.fromXML(XSTREAM.toXML(dum));

Avec cela, vous avez une copie sans avoir besoin d'implémenter une interface de clonage.

2
Jaime Hablutzel

Vous pouvez essayer d'implémenter Cloneable et utiliser la méthode clone(); Cependant, si vous utilisez la méthode clone, vous devriez, selon la norme, toujours remplacer la méthode Object's public Object clone().

1
John W

Si vous pouvez ajouter une annotation au fichier source, un processeur d’annotation ou un générateur de code tel que celui-ci peut être utilisé.

import net.zerobuilder.BeanBuilder

@BeanBuilder
public class DummyBean { 
  // bean stuff
}

Une classe DummyBeanBuilders sera générée, qui a une méthode statique dummyBeanUpdater pour créer des copies superficielles, de la même manière que vous le feriez manuellement.

DummyBean bean = new DummyBean();
// Call some setters ...
// Now make a copy
DummyBean copy = DummyBeanBuilders.dummyBeanUpdater(bean).done();
1
Lars Bohl
public class MyClass implements Cloneable {

private boolean myField= false;
// and other fields or objects

public MyClass (){}

@Override
public MyClass clone() throws CloneNotSupportedException {
   try
   {
       MyClass clonedMyClass = (MyClass)super.clone();
       // if you have custom object, then you need create a new one in here
       return clonedMyClass ;
   } catch (CloneNotSupportedException e) {
       e.printStackTrace();
       return new MyClass();
   }

  }
}

et dans votre code:

MyClass myClass = new MyClass();
// do some work with this object
MyClass clonedMyClass = myClass.clone();
1