Cloneable.clone

これまでの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