S-JIS[2007-12-21/2009-04-25] 変更履歴

Cloneable

Javaでオブジェクトの複製(コピー)が作れることを示すインターフェース。
オブジェクトのコピーが出来るクラスは、このインターフェースをimplementsする必要がある。


Object#clone()

Javaの全てのクラスの親であるObjectクラスには、clone()という、複製を作成するメソッドが用意されている。
ただしこのメソッドはprotectedなので、サブクラスでオーバーライドしてpublicにしてやらないと、外部から呼び出すことが出来ない。

Sample.java:

public class Sample {
	private String value;

	public Sample(String str) {
		this.value = str;
	}

	public String toString() {
		return value;
	}

	public Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}
	Sample s1 = new Sample("abc");
	Sample s2 = (Sample)s1.clone();
	System.out.println(s2);

これを実行すると、Object#clone()の中でjava.lang.CloneNotSupportedExceptionが発生する。
なぜかというと、Object#clone()を呼び出しているSampleクラスCloneableを実装していないから。
つまりCloneableは、Serializableと同様、“複製可能なことを示す印”だということ。

public class Sample implements Cloneable {
	〜
}

この場合、「throws CloneNotSupportedException」を削除しておくほうが使う側は便利になる。[2009-04-25]
throws句で指定する「発生し得る例外」は、増やすことは出来ないが減らすことは出来るので。

public class Sample implements Cloneable {

	@Override
	public Object clone() {	//throwsを無くす
		try {
			return super.clone();
		} catch (CloneNotSupportedException e) {
			throw new InternalError(e.toString());
		}
	}
}

なお、複製が生成される際、新しいオブジェクトが作られるけれども、そのコンストラクターが呼ばれることは無い。
(だからJavaBeanを使うときのような“デフォルトコンストラクター”等は必要ない)
Object#clone()はnativeなメソッド(つまり実行するマシン依存)であることだし、コンストラクターを呼ばないオブジェクト生成方法を使っているのだろう。


親クラスとサブクラスがあって、親クラスがCloneableを実装しているとき、親クラスのclone()を実行すればサブクラスのフィールドもちゃんと複写される

class SubClass extends Sample {
	private int number;

	public SubClass(String str, int n) {
		super(str);
		this.number = n;
	}

	public String toString() {
		return super.toString() + "/" + number;
	}
}
	SubClass s1 = new SubClass("abc", 123);
	System.out.println(s1);

	SubClass s2 = (SubClass)s1.clone();
	System.out.println(s2);

実行結果:

abc/123
abc/123

※SubClassではクローン関連は何も実装していないが、SubClassで保持している値(123)がちゃんとコピーされている。


シャローコピーとディープコピー

Object#clone()のコピー方法は、シャローコピー(shallow copy:浅いコピー)と呼ばれるもの。
これに対するのがディープコピー(deep copy:深いコピー)。

シャローコピーは、オブジェクトのフィールド(メンバー変数)がオブジェクト(参照型)である場合に、その参照をコピーするだけ。
つまりフィールドのオブジェクトは、複製元と複製先で同じオブジェクトを指すことになる。

これに対しディープコピーは、「フィールドのオブジェクト自身も複写する方式」を指す。
ディープコピーを自動的に行うメソッドは用意されていないので、オブジェクトのディープコピーを作りたいのであれば、ディープコピー用のメソッドを自分で作る必要がある。


共変戻り値型の使用

JDK1.5から共変戻り値型が使用できるようになった為、返す型にObjectでなくコピー対象のクラスを指定できるようになった。[2008-02-08]

public class CloneSample implements Cloneable {

	@Override
	public CloneSample clone() throws CloneNotSupportedException {
		return (CloneSample) super.clone();
	}
}
		CloneSample obj = new CloneSample();
		CloneSample clone = obj.clone();	//キャストが不要!

↓ついでにthrows句を無くしておくと、使う側がcatchを書かなくてよくなるので便利 [2009-04-25]

public class CloneSample implements Cloneable {

	@Override
	public CloneSample clone() {
		try {
			return (CloneSample) super.clone();
		} catch (CloneNotSupportedException e) {
			throw new InternalError(e.toString());
		}
	}
}

Cloneableの読み方

「clone」は日本語読みで「クローン」なのは大方間違いないところ。
では「Cloneable」は何と読むか?

「クローン可能」を表す英単語は、「Clonable」(cloneのeが抜ける)が正しいらしいんだな。その読みはたぶん「クローナブル」か「クローネイブル」。
なので、Cloneableは綴りに照らして「クローンエイブル」になるような気がするんだけど、元の意図を汲んで「クローナブル」「クローンナブル」と読むのかなぁ。


ちなみにCloneableは、Javaで綴りが間違っている代表例らしい…。
一時期指摘されたけど、「浸透してしまったから今さら直せない」ということでそのままになっているらしい。

でも例えばRenderContextというクラスは、メソッドの綴り間違いを直している。「メソッド名のつづりが間違っていた以前のリリースとの下位互換のために提供」なんてメソッドがある(爆)
まぁメソッドだし、間違え方が結構致命的だし、Cloneableに比べればマイナーだし、という訳で訂正したのかな。

一方で、JDK1.5から導入されたCloseableという「クローズ可能」を意味する(close()メソッドの為の)インターフェースの綴りはCloneableとそっくり。
(クローザブルと読むのか、クローズエイブル、クロージアブル?)
その路線で行くと決めたのかなぁ(苦笑)
SerializableはSerializeableじゃないけどなー


Java目次へ戻る / 新機能へ戻る / 技術メモへ戻る
メールの送信先:ひしだま