S-JIS[2007-12-21/2009-04-25] 変更履歴
Javaでオブジェクトの複製(コピー)が作れることを示すインターフェース。
オブジェクトのコピーが出来るクラスは、このインターフェースをimplementsする必要がある。
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()); } } }
「clone」は日本語読みで「クローン」なのは大方間違いないところ。
では「Cloneable」は何と読むか?
「クローン可能」を表す英単語は、「Clonable」(cloneのeが抜ける)が正しいらしいんだな。その読みはたぶん「クローナブル」か「クローネイブル」。
なので、Cloneableは綴りに照らして「クローンエイブル」になるような気がするんだけど、元の意図を汲んで「クローナブル」「クローンナブル」と読むのかなぁ。
ちなみにCloneableは、Javaで綴りが間違っている代表例らしい…。
一時期指摘されたけど、「浸透してしまったから今さら直せない」ということでそのままになっているらしい。
でも例えばRenderContextというクラスは、メソッドの綴り間違いを直している。「メソッド名のつづりが間違っていた以前のリリースとの下位互換のために提供」なんてメソッドがある(爆)
まぁメソッドだし、間違え方が結構致命的だし、Cloneableに比べればマイナーだし、という訳で訂正したのかな。
一方で、JDK1.5から導入されたCloseableという「クローズ可能」を意味する(close()メソッドの為の)インターフェースの綴りはCloneableとそっくり。
(クローザブルと読むのか、クローズエイブル、クロージアブル?)
その路線で行くと決めたのかなぁ(苦笑)
SerializableはSerializeableじゃないけどなー