Java オブジェクトがシリアル化可能であってもクローン化可能ではないのはどのような場合ですか?
-
22-09-2019 - |
質問
Java クラスが Serializable
インターフェイスはありますが、パブリックはありません clone()
メソッドを使用すると、通常は次のようなディープ コピーを作成できます。
class CloneHelper {
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T obj) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
oos.close();
byte[] bytes = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
T copy = (T) ois.readObject();
ois.close();
return copy;
} catch (ClassNotFoundException ex) {
// Shouldn't happen
throw new Error(ex);
} catch (IOException ex) {
// Probably a bug in T's custom serialization methods
throw new RuntimeException(ex);
}
}
}
私はこのようなサードパーティのライブラリ クラスに遭遇し、上記のようなハックに頼ることがよくあります。延長もしたよ ObjectOutputStream
場合によってはコピーを浅くすることもあります。非効率である (エンコード/デコードが遅く、一時的なシリアル化グラフが大量のメモリを消費する可能性がある) ことを除けば、深刻な問題が発生したことはありません。
この手法を使用するのが安全でない場合は、クラスを宣言すべきではないでしょう。 Serializable
.
それで、私が知りたいのは、あなたのクラスが Serializable
, 、パブリックの定義を妨げるものは何か clone()
メソッド (次のいずれかを使用します) Cloneable
インターフェイスまたはコピー コンストラクター?)
関連している: Javaでオブジェクトをコピーする
解決
さて、あなたは、シリアル化メカニズムは、「クローン」への1つの方法は、間接的にオブジェクトで言っています。それはもちろんそうではない、その主な機能です。通常、ネットワーク、または店舗間でオブジェクト送信プログラムをできるように使用し、後でそれを読みました。あなたがCloneableを実装オブジェクトがこの方法を使用することを期待し、Serializableを実装し、クローンにコードを期待していない間は、ローカルオブジェクト、およびない場合があります。
コードは直列化を経て、これを周囲に取り組んでいるという事実は、コードが作者か、発信者の「障害」のいずれかである著者が意図しなかった方法で、オブジェクトを、使用している示唆しているが、それはその中を意味するものではありません。一般的な直列化およびCloneableを一緒に行くます。
これとは別に、私はクローン()を正しく実装するトリッキーな限り「壊れた」されていることを確認していません。コピーコンストラクタは、右の私見を使用して取得することがより自然である。
他のヒント
私が使用するのではなく、上記のメカニズムをコピーコンストラクタを使用することを好むだろう。あなたはより細かく深くまたは浅くコピーする何を定義し、作ることができます。のコピーのの連載ののオブジェクトは異なるオブジェクトの。コピーコンストラクタこれは、ネットワークを介してシリアル化して送信対象のために適切ではない場合がある(例えば)、2つのオブジェクトがマスター・オブジェクトへの参照を共有することを可能にする。
破損Cloneable
方法が広く今みなされること注意。詳細については、ジョシュア・ブロックとこの記事を参照してください。特に、clone()
メソッドを持っていません!
Cloneableをおよそブライアンのポイントは非常に良いですが、Cloneableを右働いていた場合でも、あなたは、オブジェクトがシリアライズが、複製可能ではないようにしたいかもしれないのインスタンスが残っています。
オブジェクトは、プロセスの範囲外の一意のIDを持っている場合は、は、データベース・レコードのインメモリ表現のように、あなたはそれが同じ属性を持つ新しいレコードを作ると同等であるため、それが複製可能になりたくありません、ほとんど決して正しいことではありませんデータベースキー、などのアイデンティティ関連の属性を含みます。あなたは一つのプロセスがデータベースに話をし、これらの「ENTITY」オブジェクトを生成していてそれと同時に、あなたは、安定性または他の理由のために複数のプロセスに分割システムを有することができる(詳細のためにエリック・エヴァンスによって「ドメイン駆動設計」を参照してください。データ・バックアップアプリケーションのオブジェクトのアイデンティティの一貫性)を維持しますが、別のプロセスは、ビジネスロジックの操作を実行するために、これらのオブジェクトを使用することについての情報。それは別のプロセスから渡されるため、エンティティオブジェクトは、シリアライズする必要があります。
私はSerializableとCloneableをインターフェースは全く異なる目的のために使用されるべきだと思います。そして、あなたは、それらのそれぞれを実装する複雑なクラスを持っている場合はそう簡単ではありません。だから、一般的なケースでは、目的によって異なります。
Serializableを持つ最大の問題の一つは、彼らが簡単に不変にすることができないということです。 シリアル化の力はあなたがここに妥協を作るために。
私は、オブジェクトグラフの下コピーコンストラクタを作成するために誘惑されると思います。そのだけで行うには多くの面倒な作業をビット。
その落とし穴の数がシリアライズにあるとして、(それらのほとんどがそうですが、私は三次元のパーティのライブラリでオブジェクトをシリアライズされた場合、私はまだ彼らのためにテストしたいと思います)、危険の一種として私を打ちます。ていない可能性が共通の課題であることを、クローニング操作の一部である可能性があり、それの状態の一部として、揮発性の変数を持つオブジェクト(これはそれが可能だだけという、良いデザインではないこと)、および、そのようなAを持つことが可能ですフィールドには、シリアライズ/デシリアライズプロセスでコピーされませんでした。頭に浮かぶもう一つの問題は、列挙、定数、あなたが直列化復元時にそれらに対処していない場合には、そのようなものの複数のコピーを取得する可能性があります。
ここでも、エッジケースがありますが、気をつけたいと思い何かます。
別のケースを思い出しました - それが 列挙型.
より一般的には、クラスが実装するとき readResolve
. 。これは、返されるオブジェクトが readObject
はストリームから読み取られたものと同じではないため、必ずしも最初にストリームに書き込まれたオブジェクトのコピーであるとは限りません。