参照型変数(主に、自分で作成したクラスのオブジェクト)をコピーする場合、単純に「=」で代入するだけでは不十分な場合があります。
参照型変数の中身は参照先(オブジェクトのメモリ領域を示すポインタ)です。
「=」で代入するだけでは、参照先だけがコピーされて、参照しているものは同じという状態になるので、コピー先の変更がコピー元に影響してしまいますし、その逆にコピー元の変更がコピー先に影響してしまいます。
これを避けたい場合は、cloneメソッドを用いて中身を丸ごとコピー(新たにメモリ領域を確保し書き込み)する必要があります。
以下の記述を行うことで、cloneメソッドを使用することができるようになります。
・cloneしたいクラスでCloneableインターフェースを実装する
・Cloneableインターフェースのclone()メソッドをオーバーライドする
・clone()メソッド内で、super.clone()メソッドを使用する
(super=Objectクラス)
・super.clone()メソッドはObject型で返ってくるので自身の型でキャストを行う
・CloneNotSupportedExceptionが返ってくる可能性があるので例外処理する
以下、サンプルコードです。
参照先のみコピーした場合とcloneで中身をコピーした場合を比較しています。
参照先のみコピーした場合は、コピー後にコピー先を変更した際にコピー元が影響を受けていますが、cloneで中身をコピーした場合は影響を受けていません。
【サンプルコード】
・CloneableItem.java
// cloneしたいクラスはCloneableインターフェースを実装する必要がある
public class CloneableItem implements Cloneable {
// イミュータブルではない参照型変数を含む場合はそれも一緒にcloneが必要
// (今回は割愛)
private int itemId;
private String itemName;
public int getItemId() {
return itemId;
}
public void setItemId(int itemId) {
this.itemId = itemId;
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
// Cloneableインターフェースのclone()メソッドをオーバーライド
@Override
public CloneableItem clone() {
CloneableItem clonedItem = null;
// CloneNotSupportedExceptionを返す可能性があるので例外処理が必要
try {
// Object型で返ってくるのでキャストが必要
clonedItem = (CloneableItem)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clonedItem;
}
}
・ItemCloneMain.java
public class ItemCloneMain {
public static void main(String[] args){
System.out.println("[参照先のみコピーした場合]");
CloneableItem cloneableItem1 = new CloneableItem();
cloneableItem1.setItemId(1);
cloneableItem1.setItemName("sword");
System.out.println
("コピー前のコピー元オブジェクト:ID=" +
cloneableItem1.getItemId() +
" NAME=" +
cloneableItem1.getItemName());
CloneableItem cloneableItem2 = cloneableItem1;
System.out.println
("コピー後のコピー元オブジェクト:ID=" +
cloneableItem1.getItemId() +
" NAME=" +
cloneableItem1.getItemName());
System.out.println
("コピー後のコピー先オブジェクト:ID=" +
cloneableItem2.getItemId() +
" NAME=" +
cloneableItem2.getItemName());
cloneableItem2.setItemId(2);
cloneableItem2.setItemName("shield");
System.out.println
("編集後のコピー元オブジェクト :ID=" +
cloneableItem1.getItemId() +
" NAME=" +
cloneableItem1.getItemName());
System.out.println
("編集後のコピー先オブジェクト :ID=" +
cloneableItem2.getItemId() +
" NAME=" +
cloneableItem2.getItemName());
System.out.println("");
System.out.println("[cloneで中身をコピーした場合]");
CloneableItem cloneableItem3 = new CloneableItem();
cloneableItem3.setItemId(3);
cloneableItem3.setItemName("armor");
System.out.println
("コピー前のコピー元オブジェクト:ID=" +
cloneableItem3.getItemId() +
" NAME=" +
cloneableItem3.getItemName());
CloneableItem cloneableItem4 = cloneableItem3.clone();
System.out.println
("コピー後のコピー元オブジェクト:ID=" +
cloneableItem3.getItemId() +
" NAME=" +
cloneableItem3.getItemName());
System.out.println
("コピー後のコピー先オブジェクト:ID=" +
cloneableItem4.getItemId() +
" NAME=" +
cloneableItem4.getItemName());
cloneableItem4.setItemId(4);
cloneableItem4.setItemName("helm");
System.out.println
("編集後のコピー元オブジェクト :ID=" +
cloneableItem3.getItemId() +
" NAME=" +
cloneableItem3.getItemName());
System.out.println
("編集後のコピー先オブジェクト :ID=" +
cloneableItem4.getItemId() +
" NAME=" +
cloneableItem4.getItemName());
}
}
【実行結果】
[参照先のみコピーした場合]
コピー前のコピー元オブジェクト:ID=1 NAME=sword
コピー後のコピー元オブジェクト:ID=1 NAME=sword
コピー後のコピー先オブジェクト:ID=1 NAME=sword
編集後のコピー元オブジェクト :ID=2 NAME=shield
編集後のコピー先オブジェクト :ID=2 NAME=shield
[cloneで中身をコピーした場合]
コピー前のコピー元オブジェクト:ID=3 NAME=armor
コピー後のコピー元オブジェクト:ID=3 NAME=armor
コピー後のコピー先オブジェクト:ID=3 NAME=armor
編集後のコピー元オブジェクト :ID=3 NAME=armor
編集後のコピー先オブジェクト :ID=4 NAME=helm