シャローコピーは新しいオブジェクトを作成しますが、ネストされたオブジェクトの参照をコピーするだけで、それ自体をコピーしません。ディープコピーは新しいオブジェクトを作成し、元のオブジェクトにネストされたすべてのオブジェクトを再帰的にコピーします。そのため、コピーと元のオブジェクトの間には共通の参照がありません。
使用するタイミング:
実装例:
// ネストされたオブジェクトを持つクラスの例 class Address { String city; Address(String city) { this.city = city; } } class Person implements Cloneable { String name; Address address; Person(String name, Address address) { this.name = name; this.address = address; } // シャローコピー public Person clone() throws CloneNotSupportedException { return (Person) super.clone(); } // ディープコピー public Person deepClone() { return new Person(this.name, new Address(this.address.city)); } }
clone()メソッドを使用してディープコピーを実装できますか?
答え:
いいえ、標準のObject.clone()メソッドは「シャローコピー」しか実装していません。ディープコピーを取得するには、すべてのネストされたフィールドを手動でクローンするか、外部ライブラリ(例えばApache Commons Lang)を使用する必要があります。
例:
@Override public Person clone() throws CloneNotSupportedException { Person cloned = (Person) super.clone(); cloned.address = new Address(this.address.city); // 手動でディープコピー return cloned; }
逸話
大規模なCRMプロジェクトで、
super.clone()を通じてオブジェクトのコピーを実装した後、開発者はコピーが完全に独立していると期待していましたが、コピーしたユーザーの住所を変更すると、元の住所も変更されてしまいました。バグはリリース後に発覚し、顧客データベースに混乱を引き起こしました。
逸話
シリアル化/デシリアル化のためにディープコピーの標準メカニズムSerializableを選択しましたが、ネストされたオブジェクトのフィールドの一つをSerializableとして宣言するのを忘れたため、実行中にエラー(NotSerializableException)が発生し、バックアップ時にデータの損失が生じました。
逸話
あるマイクロサービスで、DTOをコピーするためにBeanUtils.copyPropertiesライブラリを使用していましたが、このメカニズムがシャローコピーしか実装していないことに気づきませんでした。その後のリファクタリングで、ネストされたコレクションの変更がデータベースから取得した元のデータに影響を与え、多くの不明瞭なバグが発生しました。