Javaのオブジェクトのクローンについて
このページでは、Javaでオブジェクトをクローンする方法について解説します。各セクションにジャンプできるリンクを以下に用意しています。
- オブジェクトをクローンする理由
- Cloneable インターフェースの役割
- 浅いコピー(Shallow Cloning)
- 深いコピー(Deep Cloning)
- クローンの実例
- クローンの制限と課題
- クローンの代替方法
オブジェクトをクローンする理由
Javaプログラムでは、あるオブジェクトをそのままコピーしたい場合があります。その目的は以下の通りです。
- オリジナルのオブジェクトを変更せず、別のオブジェクトを生成して独立して操作する。
- データ構造の複製やバックアップを作成する。
- オブジェクトの状態を保存し、元に戻せるようにする。
Cloneable インターフェースの役割
Javaでは、java.lang.Cloneable
インターフェースを使用してオブジェクトをクローンできます。このインターフェースを実装することで、オブジェクトのクローンを許可する意図を示します。
重要なポイント:
Cloneable
はマーカーインターフェースであり、メソッドを提供しません。Object
クラスのclone()
メソッドをオーバーライドする必要があります。
使用例
class Example implements Cloneable {
public int value;
public Example(int value) {
this.value = value;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Main {
public static void main(String[] args) {
try {
Example original = new Example(10);
Example clone = (Example) original.clone();
System.out.println("Original value: " + original.value);
System.out.println("Cloned value: " + clone.value);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
浅いコピー(Shallow Cloning)
浅いコピーは、オブジェクトのフィールドをそのままコピーしますが、参照型フィールドは新しい参照先を作成せず、元のオブジェクトの参照を保持します。
例:
class Person implements Cloneable {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Person original = new Person("Alice", 30);
Person clone = (Person) original.clone();
clone.name = "Bob";
System.out.println("Original name: " + original.name); // Alice
System.out.println("Cloned name: " + clone.name); // Bob
}
}
深いコピー(Deep Cloning)
深いコピーでは、オブジェクトの全てのフィールドが再帰的にクローンされます。これにより、参照型フィールドも新しいオブジェクトとして生成されます。
例:
class Address implements Cloneable {
public String city;
public Address(String city) {
this.city = city;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Employee implements Cloneable {
public String name;
public Address address;
public Employee(String name, Address address) {
this.name = name;
this.address = address;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Employee cloned = (Employee) super.clone();
cloned.address = (Address) address.clone();
return cloned;
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("Tokyo");
Employee original = new Employee("John", address);
Employee clone = (Employee) original.clone();
clone.address.city = "Osaka";
System.out.println("Original city: " + original.address.city); // Tokyo
System.out.println("Cloned city: " + clone.address.city); // Osaka
}
}
クローンの実例
- オブジェクトの履歴管理: 変更前の状態を保持。
- データのシミュレーション: 元データを変更せずに実験。
- 設計パターン: Prototypeパターンでの活用。
クローンの制限と課題
Cloneable
インターフェースの柔軟性が低い。- 深いコピーはコードが複雑になる。
- クローンの動作が予測しにくい場合がある。
クローンの代替方法
- コピーコンストラクタを使用。
- シリアライズとデシリアライズを使用。
- 外部ライブラリ(Apache Commons Langの
SerializationUtils
など)を使用。