web-dev-qa-db-ja.com

Javaでオブジェクトをコピーする方法

以下のコードを見てください。

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'

そのため、dumdumtwoにコピーし、dumに影響を与えずにdumtwoを変更したいと思います。しかし、上記のコードはそれをしていません。 dumで何かを変更すると、dumtwoでも同じ変更が行われます。

dumtwo = dumと言うと、Javaは 参照のみ をコピーします。それで、dumの新しいコピーを作成し、それをdumtwoに割り当てる方法はありますか?

728
Veera

コピーコンストラクタを作成します。

class DummyBean {
  private String dummy;

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

すべてのオブジェクトには、オブジェクトのコピーに使用できるcloneメソッドもありますが、使用しないでください。クラスを作成して不適切なクローンメソッドを作成するのはとても簡単です。それをするつもりなら、少なくともJoshua Blochがそれについて言っていることをEffective Javaに読んでください。

560
egaga

基本: Javaでのオブジェクトのコピー。

2つのオブジェクト、 includedObj1 および includedObj2 を含むobject- obj1を想定します。 
enter image description here

浅いコピー:
シャローコピーは、同じクラスの新しいinstanceを作成し、すべてのフィールドを新しいインスタンスにコピーして返します。 オブジェクトクラス cloneメソッドを提供し、シャローコピーをサポートします。
enter image description here

ディープコピー:
ディープコピーは、オブジェクトが、それが参照しているオブジェクトと一緒にコピーされるの場合に発生します。以下の画像は、ディープコピーが実行された後のobj1を示しています。 obj1がコピーされただけではないしかし、その中に含まれるオブジェクトも同様にコピーされています。 Java Object Serializationを使ってディープコピーを作成することができます。残念ながら、この方法にもいくつか問題があります( 詳細な例 )。 
enter image description here

考えられる問題:
cloneは正しく実装するのが難しいです。
防衛的なコピーコピーコンストラクタ (@egagaの回答として)、または 静的ファクトリメソッド を使用することをお勧めします。

  1. もしあなたがオブジェクトを持っていて、あなたが公にclone()メソッドを持っていることを知っているけれども、あなたがコンパイル時にオブジェクトのタイプを知らないならば、あなたは問題を抱えています。 JavaにはCloneableというインターフェースがあります。実際には、オブジェクトをCloneableにしたい場合は、このインタフェースを実装する必要があります。 Object.clone protected なので、アクセスできるようにするにはパブリックメソッドでオーバーライドする必要があります。
  2. ディープコピー of 複合オブジェクトを試すときに、別の問題が発生します。すべてのメンバオブジェクト変数のclone()メソッドもディープコピーを実行すると仮定します。これは仮定のリスクが高すぎるためです。あなたはすべてのクラスでコードを制御しなければなりません。

たとえば org.Apache.commons.lang.SerializationUtils には、シリアライゼーションを使用したディープクローン用のメソッドがあります( Source )。 Beanのクローンを作成する必要がある場合は、 org.Apache.commons.beanutilsSource )に2つのユーティリティメソッドがあります。

  • Beanクラス自体がCloneableを実装していなくても、cloneBeanは、利用可能なプロパティの取得メソッドおよび設定メソッドに基づいてBeanを複製します。
  • copyPropertiesは、プロパティ名が同じである場合はすべて、OriginのBeanから宛先のBeanにプロパティ値をコピーします。
374
Chandra Sekhar

パッケージimport org.Apache.commons.lang.SerializationUtils;にはメソッドがあります。

SerializationUtils.clone(Object);

例:

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

以下のようにしてください。

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

別のオブジェクトを入手したい場合は、単純にクローン作成を実行してください。例: 

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

Reflection APIを使用しても回答がないのはなぜですか?

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

とても簡単です。

編集:再帰を介して子オブジェクトを含める

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

GoogleのJSONライブラリを使用してシリアル化し、シリアル化されたオブジェクトの新しいインスタンスを作成します。いくつかの制限付きでディープコピーを行います。

  • 再帰的参照はあり得ません

  • 異種の型の配列はコピーされません

  • 配列とリストは型付けされるべきであり、そうでなければインスタンス化するクラスを見つけることができません

  • 自分で宣言したクラスに文字列をカプセル化する必要があるかもしれません。

また、このクラスを使用して、ユーザー設定、ウィンドウ、および実行時に再ロードする必要がないものを保存します。使い方はとても簡単で効果的です。

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

はい、あなたはただオブジェクトを参照しています。 Cloneableが実装されていれば、オブジェクトを複製することができます。

オブジェクトのコピーに関するこのウィキの記事をチェックしてください。

こちらを参照してください。オブジェクトのコピー

22
Chrisb

はい。 ディープコピー あなたのオブジェクトが必要です。

13
bruno conde

クラスにCloneable以下のコードを追加します。

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

これを使用してくださいclonedObject = (YourClass) yourClassObject.clone();

12
Teja Maridu

これが必要になった場合のclone()のわかりやすい説明です。

ここ:clone(Javaメソッド)

10
Jon Bringhurst

これもうまくいきます。想定モデル

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

最初に compile 'com.google.code.gson:gson:2.8.1'をあなたのアプリに追加してください。それから

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

アクセス修飾子の後にtransientキーワードを使用すると、フィールドの使用を除外できます。

注: これは悪い習慣です。また、CloneableJavaSerializationの使用はお勧めしません。遅くて壊れています。最高のパフォーマンスのためのコピーコンストラクタを書く ref

何かのようなもの

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

90000反復のテスト統計:
UserAccount clone = gson.fromJson(gson.toJson(aO), UserAccount.class); 808ms を取ります

UserAccount clone = new UserAccount(aO); 1ms 以下になります

結論: 上司が頭がおかしくてスピードが好きならgsonを使ってください。あなたが品質を好むならば、セカンドコピーコンストラクタを使用してください。

Android Studioではコピーコンストラクタコード ジェネレータプラグイン を使用することもできます。

8
Qamar

あなたの答えはディープクローニングで、これにはCloneableインターフェースを実装し、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;
   }
}

これを DummyBean dumtwo = dum.clone();と呼びます。

7
abbas

そのためには、何らかの方法でオブジェクトを複製する必要があります。 Javaにはクローン作成メカニズムがありますが、必要でなければ使用しないでください。コピーがうまくいくようなコピー方法を作成してから、次のようにします。

dumtwo = dum.copy();

ここで は、コピーを完成させるためのさまざまなテクニックに関するより多くのアドバイスです。

7
Yishai

ディープクローニングユーティリティを使用します。 

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

これはどんなJavaオブジェクトでも深くコピーするでしょう、 https://github.com/kostaskougios/cloning でそれをチェックしてください。

7
Cojones

明示的にコピーする以外に、他のアプローチはオブジェクトを不変にすることです(setや他のミューテーターメソッドはありません)。このようにして問題は起こりません。不変性は大きなオブジェクトではより難しくなりますが、それとは反対に、一貫性のある小さなオブジェクトとコンポジットに分割する方向に進むということです。

class DB {
  private String dummy;

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

コピーしたいオブジェクトを渡して、必要なオブジェクトを取得します。

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;

    }

objDestを目的のオブジェクトに解析します。

ハッピーコーディング!

2
A-Droid Tech

http://x-stream.github.io/ - から、XStreamを使って自動的にディープコピーすることができます。

XStreamはオブジェクトをXMLにシリアライズし、またに戻すためのシンプルなライブラリです。

それをあなたのプロジェクトに追加してください(mavenを使用している場合)

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

それから

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

これであなたはどんなクローニングインタフェースも実装する必要なしでコピーを持っています。

2
Jaime Hablutzel

Cloneableを実装してclone()メソッドを使うことができます。ただし、cloneメソッドを使用する場合は、必ず標準でObjectpublic Object clone()メソッドをオーバーライドしてください。

1
John W

注釈をソースファイルに追加できる場合は、 this this のような注釈プロセッサまたはコードジェネレータを使用できます。

import net.zerobuilder.BeanBuilder

@BeanBuilder
public class DummyBean { 
  // bean stuff
}

手動で行うのと同じ方法で、シャローコピーを作成するための静的メソッドDummyBeanBuildersを持つクラスdummyBeanUpdaterが生成されます。

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

  }
}

そしてあなたのコードで:

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