Javaにおけるshallow copy(浅いコピー)は、オブジェクトをコピーする際に、ネストされたオブジェクトへの参照のみをコピーし、実際のオブジェクトはコピーしません。このため、ネストされたオブジェクトの変更はコピーとオリジナルの両方に反映されます。一方、deep copy(深いコピー)は、すべてのネストされたオブジェクトの新しい独立したコピーを作成することを意味します。
class Address implements Cloneable { String city; Address(String city) { this.city = city; } protected Object clone() throws CloneNotSupportedException { return super.clone(); } } class Person implements Cloneable { String name; Address address; Person(String name, Address address) { this.name = name; this.address = address; } protected Object clone() throws CloneNotSupportedException { return super.clone(); } } // 使用例 Person p1 = new Person("Ivan", new Address("Moscow")); Person p2 = (Person) p1.clone(); p2.address.city = "Petersburg"; System.out.println(p1.address.city); // 出力:「Petersburg」-浅いコピー
class Address implements Cloneable { String city; Address(String city) { this.city = city; } protected Object clone() throws CloneNotSupportedException { return new Address(this.city); } } class Person implements Cloneable { String name; Address address; Person(String name, Address address) { this.name = name; this.address = address; } protected Object clone() throws CloneNotSupportedException { Person cloned = (Person) super.clone(); cloned.address = (Address) address.clone(); return cloned; } }
深いコピーにより、オブジェクトの完全に独立したコピーを作成できます。
質問: clone()メソッドを使用して、任意の複雑なオブジェクトの深いコピーを保証することはできますか?
回答: できません。標準のclone()は、浅いコピー(shallow copy)しか行いません。深いコピーを行うには、すべてのネストされたクラスでclone()をオーバーライドし、すべての参照フィールドを明示的にクローンする必要があります。また、多くのコレクションや標準クラスは深いクローンをサポートしていません。
物語
ユーザーとそのアドレスのコレクションがclone()メソッドを使用して浅いコピーでクローンされました。あるユーザーのアドレスを変更すると、別のユーザーのアドレスも変更されました。なぜなら、参照がコピーされたからです。データがユーザー間で「流出」しました。
物語
入れ子になったコレクションを持つ複雑なオブジェクトをシリアル化する際に、深いコピーではなくclone()を使用しました。シリアル化から復元した後、データが不整合になり、NPEが発生しました。
物語
REST APIでは、DTOオブジェクトがデフォルトでBeanUtils.copyProperties()を介してクローンされ、クライアント間で同じコレクションが分かれてしまいました。その結果、ユーザーは他人のデータを見たり編集したりできました。