Javaでインスタンスのディープクローン/コピーを実行する推奨方法があるかどうか疑問に思っています。
私は3つの解決策を考えていますが、私はいくつかを見逃すことができます、そしてあなたの意見が欲しいです
編集:Bohzoの提案と洗練された質問を含める:浅いクローニングよりも深いクローニングについてです。
手動でプロパティの後にクローンをコーディングし、可変インスタンスもクローンされていることを確認します。
pro:
-実行される内容の制御
-迅速な実行
cons:
-作成と保守に手間がかかる
-バグを起こしやすい(コピー/貼り付けの失敗、プロパティの欠落、変更可能なプロパティの再割り当て)
独自のリフレクションツールまたは外部ヘルパー(jakarta common-beansなど)を使用すると、1行でジョブを実行する一般的なコピーメソッドを簡単に記述できます。
pro:
-書きやすい
-メンテナンスなし
cons:
-何が起こるかの制御が少ない
-リフレクションツールがサブオブジェクトのクローンも作成しない場合、可変オブジェクトで起こりやすいバグ
-実行速度の低下
次のようなフレームワークを使用してください。
commons-lang SerializationUtils
Java Deep Cloning Library
ドーザー
Kryo
pro:
-リフレクションと同じ
-正確に複製されるものをより詳細に制御します。
cons:
-すべての可変インスタンスは、階層の最後であっても完全に複製されます
-実行に非常に時間がかかる可能性があります
javassit 、 BCEL または cglib は、片手で書かれた速度で専用のクローンを生成するために使用できます。誰かがこれらのツールのいずれかをこの目的に使用しているライブラリを知っていますか?
私がここで見逃したものは何ですか?
どちらをお勧めしますか?
ありがとう。
commons-lang SerializationUtils -シリアル化の使用-すべてのクラスがコントロール内にあり、Serializable
の実装を強制できる場合。
Java Deep Cloning Library -リフレクションを使用する-クローンを作成するクラスまたはオブジェクトが制御不能(サードパーティライブラリ)であり、Serializable
またはinを実装できない場合Serializable
を実装したくない場合。
commons-beanutils BeanUtils -ほとんどの場合。
Spring BeanUtils -すでにspringを使用しているため、クラスパスにこのユーティリティがある場合。
私は「do-it-yourself」オプションを意図的に省略しました-上記のAPIは、クローンするものとしないものを適切に制御します(たとえば、transient
またはString[] ignoreProperties
を使用)、ホイールの再発明は推奨されません。
Joshua Blochの本には というタイトルの章全体があります。「項目10:クローンを慎重にオーバーライドする」 で、ほとんどの部分でクローンをオーバーライドすることが悪い考えである理由を説明しています。 Java仕様が多くの問題を引き起こすためです。
彼はいくつかの選択肢を提供します:
コンストラクターの代わりにファクトリーパターンを使用します。
public static Yum newInstance(Yum yum);
コピーコンストラクターを使用します。
public Yum(Yum yum);
Javaのすべてのコレクションクラスはコピーコンストラクターをサポートします(例:new ArrayList(l);)
バージョン2.07以降 Kryoは浅い/深いクローンをサポートしています :
Kryo kryo = new Kryo();
SomeClass someObject = ...
SomeClass copy1 = kryo.copy(someObject);
SomeClass copy2 = kryo.copyShallow(someObject);
Kryoは高速です。Kryoのページでは、本番環境で使用している会社のリストを見つけることができます。
XStream toXML/fromXMLをメモリで使用します。非常に高速で、長い間存在しており、強くなっています。オブジェクトはSerializableである必要はなく、リフレクションを使用する必要もありません(ただし、XStreamは必要です)。 XStreamは、同じオブジェクトを指す変数を識別でき、誤ってインスタンスの2つの完全なコピーを作成することはありません。そのような詳細の多くは、長年にわたって打ち出されてきました。私は何年もそれを使用してきましたが、それは行き先です。想像できるほど簡単に使用できます。
new XStream().toXML(myObj)
または
new XStream().fromXML(myXML)
クローンを作成するには、
new XStream().fromXML(new XStream().toXML(myObj))
より簡潔に:
XStream x = new XStream();
Object myClone = x.fromXML(x.toXML(myObj));
複雑なオブジェクトの場合、およびパフォーマンスがそれほど重要でない場合は、 gson を使用してオブジェクトをjsonテキストにシリアル化し、テキストを逆シリアル化して新しいオブジェクトを取得します。
transient
フィールドはコピーされず、原因がStackOverflowError
の循環参照を持つオブジェクトを除き、リフレクションに基づくgsonはほとんどの場合に機能します。
public static <ObjectType> ObjectType Copy(ObjectType AnObject, Class<ObjectType> ClassInfo)
{
Gson gson = new GsonBuilder().create();
String text = gson.toJson(AnObject);
ObjectType newObject = gson.fromJson(text, ClassInfo);
return newObject;
}
public static void main(String[] args)
{
MyObject anObject ...
MyObject copyObject = Copy(o, MyObject.class);
}
依存します。
速度を上げるには、DIYを使用してください。防弾のために、反射を使用します。
ところで、一部のオブジェクトはオーバーライドされたシリアル化メソッド(readObject/writeObject)を提供し、バグが発生する可能性があるため、シリアル化は反射と同じではありません
良いhashCode()およびequals()メソッドと組み合わせて、単体テストで簡単に証明できるDIYの方法をお勧めします。
Object.clone()をオーバーライドし、最初にsuper.clone()を呼び出し、ディープコピーするすべての参照でref = ref.clone()を呼び出すことをお勧めします。多かれ少なかれ自分でやるアプローチですが、少しコーディングが必要です。