これまでのcloneの実装だと、浅いコピーになるため、正しい深いコピーの実装をする。
Cloneable (Java Platform SE 6)
Javaのcloneメソッドの正しい実装方法 - Qiita
厳密には、Cloneableにはcloneメソッドが含まれない模様。
(なのでタイトルはおかしいが、実質そういうような感じなので。)
SubObjectを追加し、
// サブオブジェクト class SubObject{ // フィールド int data; // int型フィールドdata // メソッド public void setData(int data) { // dataをセットするメソッドsetData. this.data = data; // メンバに引数をセット. } @Override public String toString(){ // オーバーライドメソッドtoString. String retStr = "SubObject[data = " + data + "]"; // retStrにメンバフィールドの値をセット. return retStr; // retStrを返す. } }
新たにint型のdataを持つ。
// カスタムオブジェクト class CustomObject implements Cloneable{ // Objectの派生クラスCustomObject(Cloneableを実装.) // フィールド String str; // Stringクラスフィールドstr. int value; // int型フィールドvalue. SubObject subObj; // SubObjectクラスフィールドsubObj. // メソッド public void setStr(String str) { // strをセットするメソッドsetStr. this.str = str; // メンバに引数をセット. } public void setValue(int value) { // valueをセットするメソッドsetValue. this.value = value; // メンバに引数をセット. } public void setSubObj(SubObject subObj){ // subObjをセットするメソッドsetSubObj. this.subObj = subObj; // メンバに引数をセット. } public void setData(int data){ // subObjのdataをセットするメソッドsetData. this.subObj.data = data; // メンバのメンバに引数をセット. } @Override public String toString(){ // オーバーライドメソッドtoString. String retStr = "CustomObject[str = " + str + ", " + "value = " + value + ", subObj.data = " + subObj.data + "]"; // retStrにメンバフィールドの値をセット. return retStr; // retStrを返す. } @Override public Object clone(){ // オブジェクトのクローンをするオーバーライドメソッドclone. try{ return (CustomObject)super.clone(); // 親クラス(Object)のcloneを呼ぶ. } catch (CloneNotSupportedException e){ throw new InternalError(e.getMessage()); // InternalErrorに投げる. } } }
これをCustomObjectのメンバに追加。
//メインクラス class MainClass { // MainClassクラスの定義 // Javaのエントリポイント public static void main(String[] args) { // mainメソッドの定義 // CustomObjectを1つ生成. CustomObject obj1 = new CustomObject(); // CustomObjectオブジェクトobj1を生成. // 文字列をセット. obj1.setStr("ABC"); // obj1.setStrで"ABC"をセット. // 整数値をセット. obj1.setValue(123); // obj1.setValueで123をセット. // サブオブジェクトの生成. SubObject subObj1 = new SubObject(); // SubObjectオブジェクトsubObj1を生成. subObj1.setData(100); // subObj1.setDataで100をセット. // subObj1のセット. obj1.setSubObj(subObj1); // obj1.setSubObjでsubObj1をセット. // obj1の中身を出力. System.out.println("obj1.toString() = " + obj1.toString()); // obj1.toString()を出力. // CustomObjectオブジェクトをもうひとつ用意. CustomObject obj2; // obj2を用意. // クローン. obj2 = (CustomObject)obj1.clone(); // obj1.cloneでobj2にクローン. // obj2の内容を変更. obj2.setStr("XYZ"); // obj2.setStrで"XYZ"をセット. obj2.setValue(789); // obj2.setValueで789をセット. obj2.setData(200); // obj2.setDataで200をセット. // obj2の中身を出力. System.out.println("obj2.toString() = " + obj2.toString()); // obj2.toString()を出力. // obj1の中身を出力. System.out.println("obj1.toString() = " + obj1.toString()); // obj1.toString()を出力. } }
これでクローンをすると、
obj1.toString() = CustomObject[str = ABC, value = 123, subObj.data = 100] obj2.toString() = CustomObject[str = XYZ, value = 789, subObj.data = 200] obj1.toString() = CustomObject[str = ABC, value = 123, subObj.data = 200]
subObjのほうは、クローンしても参照をコピーしてるので、obj2の変更の影響がobj1に出てしまう。
Cloneableでcloneを実装し、
自身をクローンした後、subObj分もクローンしてもらって、それを返すようにする。
obj1.toString() = CustomObject[str = ABC, value = 123, subObj.data = 100] obj2.toString() = CustomObject[str = XYZ, value = 789, subObj.data = 200] obj1.toString() = CustomObject[str = ABC, value = 123, subObj.data = 100]
obj2の変更が、obj1には影響しないようになった。
Sample/java/Cloneable/clone/src/Cloneable at master · bg1bgst333/Sample · GitHub