- PR -

Object#clone()メソッドについて

投稿者投稿内容
Guri
会議室デビュー日: 2007/01/16
投稿数: 12
投稿日時: 2007-02-28 16:22
Object#clone()メソッドについて尋ねたいです。
Cloneableを実装した独自クラスを作成せずに、簡便にListやMapのclone()を実行する方法はないでしょうか?

下記のようなコードがあったとして、clone()可能ならば、clone()を呼び出すような方法はあるでしょうか。

コード:
import java.util.*;

class Test{
 public static void main(String[] args){
  //これをlistがclone()可能ならば、clone()のようにしたい clone()の実装がシャローコピーでもディープコピーでも
  List<Integer> list = new java.util.Listを実装した具象クラス(ArrayList等)<Integer>();
  Map<Integer, Integer> map = new java.util.Mapを実装した具象クラス(HashMap等)<Integer, Integer>();
  /*
  こうできればよいけれど、Cloneableはclone()メソッドを持たないので当然コンパイルエラー。
  if(list instanceof Cloneable){
   ((Cloneable)list).clone();
  }
  */
  //((ArrayList)list).clone(); //これは当然可能
  //java.util.CollectionもCloneableを実装しているわけではないので、((Collection)list).clone()は不可。
  //Object#clone()はprotectedのため、コンパイルできない
  //((Object)list).clone();
 }
}



独自で作成したinterfaceだったり、既存のinterfaceを拡張して、Cloneableを実装すれば、こと足りますし、
Listを内包するようなCloneableを実装したクラスを作成すればよいのは分かります。
いままでは、そうしてきたのですが、何だかすっきりしないもので。
私が知らないだけで、実はうまい実装方法があるのかと思い、尋ねてみました。

宜しくお願いいたします。

三等兵
会議室デビュー日: 2006/11/14
投稿数: 10
投稿日時: 2007-02-28 16:30
java.util.Collections#copy()
なんてものでは要件は満たせないでしょうか?
使ったことがないのでちょっと自信ないです。
Guri
会議室デビュー日: 2007/01/16
投稿数: 12
投稿日時: 2007-02-28 16:38
三等兵さん 回答ありがとうございます。

引用:

三等兵さんの書き込み (2007-02-28 16:30) より:
java.util.Collections#copy()
なんてものでは要件は満たせないでしょうか?
使ったことがないのでちょっと自信ないです。



回答頂いたのに、言葉が足りず、本当に申し訳ありません。

例として、java.util.Listをあげたのですが、
本当のところは、対象オブジェクトがクローン可能であれば、clone()を実行するようなコードが書きたいのです。

[ メッセージ編集済み 編集者: Guri 編集日時 2007-02-28 16:39 ]
びしばし
大ベテラン
会議室デビュー日: 2002/03/13
投稿数: 181
投稿日時: 2007-02-28 17:13
ただclone()を実行して、どうするのですか ?
複製した別インスタンスが欲しいんじゃないんですか ?

で、そういうときはコピーコンストラクタを利用/用意すると思いますが、どうでしょう ?

以上、「Effective Java」の受け売りです。
だっちょ
大ベテラン
会議室デビュー日: 2006/12/05
投稿数: 115
投稿日時: 2007-02-28 17:14
使ったことはないのでどの程度使えるのか知りませんが、任意のObjectに対しObjectInputStreamに書き込んで復元すればとりあえずcloneしたことになるのでは?
ByteArrayOutputSteam bous = new ByteArrayOutputSteam();
ObjectOutputStream ous = new ObjectOutputStream(bous);
ous.writeObject(object);
ous.close();
ByteArrayInputSteam bins = new ByteArrayInputSteam(bous.toByteArray());
Object copy = new ObjectInputStream(bins).readObject();
java.util.ArrayListだとコピーできているようです。



[ メッセージ編集済み 編集者: だっちょ 編集日時 2007-02-28 17:24 ]

[ メッセージ編集済み 編集者: だっちょ 編集日時 2007-02-28 17:26 ]
sawat
大ベテラン
会議室デビュー日: 2006/08/02
投稿数: 112
投稿日時: 2007-02-28 17:33
Cloneableはjavaの標準ライブラリの中でも、ワースト5に入るぐらいまずい設計だとおもいますが、どうしても必要なら、以下のようにリフレクションを使ってやるのがよいと思います。

コード:

public static <T> T clone(T obj) throws CloneNotSupportedException {
try {
Class<? extends T> clazz = obj.getClass();
Method method = clazz.getMethod("clone");
return clazz.cast(method.invoke(obj));
} catch (final Exception e) {
throw new CloneNotSupportedException() {
@Override
public Throwable getCause() {
return e;
}
};
}
}



このコードでは、呼び出し元はCloneNotSupportedExceptionをキャッチしなければならないので使い勝手が悪いですが、引数に渡すオブジェクトがclone可能であることが保障できる(そうでない場合は呼び出しもとのバグであるとする)場合は、CloneNotSupportedExceptionの代わりに非チェック例外であるIllegalArugumentExceptionやAssertionErrorを投げるのもありかと思います。

# 修正
# クローンの型チェックを追加。
# さらに、Class.castを使うように再修正。


[ メッセージ編集済み 編集者: sawat 編集日時 2007-02-28 18:12 ]
Guri
会議室デビュー日: 2007/01/16
投稿数: 12
投稿日時: 2007-02-28 18:17
回答ありがとうございます。

ビシバシ様
引用:

ただclone()を実行して、どうするのですか ?
複製した別インスタンスが欲しいんじゃないんですか ?


意味がつかめないのですが、
私が書いた((Cloneable)list).clone();の部分のことでしょうか?
これは、便宜上、Object obj = ((Cloneable)list).clone();を略しました。
それとも違う意図でしょうか。

引用:

で、そういうときはコピーコンストラクタを利用/用意すると思いますが、どうでしょう ?


ええ、独自に作成したクラスならば、そうするかもしれません。

だっちょ様
設計を変更して、お茶を濁しましたが、以前書いたコードでは、
こんな風に書こうとしました。

コード:

Object obj = 何かのオブジェクト。
if(obj instanceof Cloneable){
//clone()の実装によるけれど、clone()の方が、Serializeの方が
//おそらく処理が軽いため、先にcloneの判定
//ここは、今回同様、実装方法が分かりませんでした。
}else if(obj instanceof Serializable){
//だっちょ様に掲示して頂いたコードと同様の処理
//メモリ上でディープコピー
}else{
//...
}


sawat様
reflectionがありましたか。。
参考になりました。

因みにワースト5の残り4つは何でしょうか。

私はSerializableを入れたいのですが。
今まで遭遇して最悪だったのが、java.awt.AlphaComposite(finalクラスなうえにSerializableを実装していない。writeObject()も実装できない。)をシリアライズしようとしたときです。

java.awt.GradientPaintもなかなかのものですが。
同じjava.awt.Paint実装クラスでもjava.awt.ColorはSerializableを実装しているのに。。
Serializableを実装し忘れているだけの気がするのですが。


[ メッセージ編集済み 編集者: Guri 編集日時 2007-02-28 18:18 ]
sawat
大ベテラン
会議室デビュー日: 2006/08/02
投稿数: 112
投稿日時: 2007-02-28 18:43
引用:

Guriさんの書き込み (2007-02-28 18:17) より:
因みにワースト5の残り4つは何でしょうか。



えーと、その辺はあまり具体的に考えずに書きました。
(つまり、そのぐらいイケてないCloneableは極力避けて通るべきだという意味)

個人的に気になるのを挙げるなら、

  • RuntimeExceptionがExceptionのサブクラスであること。
  • java.util.Dateやjava.awt.Pointなど不変クラスであるべきいくつかのクラスが可変であること。
  • AWTとSwingで2つのGUIツールキットがあること。
他にもあった気がするけど、今思いつくのはこんなところ。

Serializableに比べればどれも実害は少なそうですね。

スキルアップ/キャリアアップ(JOB@IT)