シリアライゼーションで古いバージョンのクラス/インターフェースをサポートする方法をいくつか紹介します。
マイグレーターを使用する
このような場合、Java プロジェクトで以下が必要になります。
- これらすべてのクラスを統合する不変のインターフェース
- として注釈が付けられたそのインターフェースの古い実装
@Deprecated
- インターフェイスの新しい実装
- 廃止されたオブジェクトを新しいオブジェクトに変換するのに役立つ Migrator クラス
最初から、古いバージョンのオブジェクトを操作できるとは限らないことをお伝えします (シリアライズ可能なオブジェクトのバージョン管理に関する Oracle のドキュメントを参照してください)。簡単にするために、アップグレード中、クラスは常に定義したインターフェースを実装すると仮定しますIEntity
。
public interface IEntity extends Serializable {
// your method definitions
}
そして、最初に次のクラスで作業するとします。
public class Entity implements IEntity {
private static final long serialVersionUID = 123456789L;
// fields and methods
}
Entity
クラスを新しい serialVersionUID を持つ新しい実装にアップグレードする必要がある場合は、最初に注釈を付けます@Deprecated
が、名前を変更したり、別のパッケージに移動したりしないでください。
@Deprecated
public class Entity implements IEntity {
private static final long serialVersionUID = 123456789L;
// fields and methods
}
次のように、新しい serialVersionUID (重要) と追加のコンストラクターを使用して、新しい実装を作成します...
public class Entity_new implements IEntity {
private static final long serialVersionUID = 5555558L;
public Entity_new(IEntity){
// Create a new instance of Entity_new copying the given IEntity
}
}
もちろん、手順全体の中で最も重要な部分は、上記のコンストラクターをどのように実装するかです。古いタイプのいくつかのオブジェクトをEntity
(たとえば を使用してObjectOutputStream
) バイナリ ファイルとしてシリアライズし、 に移行したEntity_new
場合、それらを のインスタンスとして解析してから、 のインスタンスにEntity
変換できますEntity_new
。次に例を示します。
public class Migrator {
private final IEntity entity;
private Class<? extends IEntity> newestClass = Entity_new.class;
public Migrator(final IEntity entity){
this.entity = entity;
}
public Migrator setNewestClass(Class<? extends IEntity> clazz){
this.newestClass = clazz;
return this;
}
public IEntity migrate() throws Exception {
Constructor<? extends IEntity> constr =
newestClass.getConstructor(IEntity.class);
return constr.newInstance(this.entity);
}
}
もちろん、その特定のコンストラクターを必要としない、またはJava リフレクションを使用する他の代替手段があります。選択できる設計アプローチは他にもたくさんあります。上記のコードでは、簡単にするために、例外処理と null オブジェクトのチェックが完全に省略されていることにも注意してください。
シリアル化可能なジェネリック インターフェイスを設計する
該当する場合は、最初に、将来変更される可能性が低いクラスのインターフェイスを設計することを試みることができます。変更される可能性が非常に高い一連のプロパティをクラスに格納する必要がある場合はMap<String, Object>
、この目的でを使用することを検討してください。String
はプロパティ名/識別子を参照し、Object
は対応する値です。
readObject と writeObject をカスタマイズする
古いバージョンのサポートを提供する方法は他にもありますが、完全を期すために言及しますが、私が選択する方法はありません。クラス/インターフェースの現在および以前のすべてのバージョンの両方に対応する方法でprivate void readObject(ObjectInputStream in)
実装できます。private void writeObject(ObjectOutputStream out)
このようなことが常に実行可能で持続可能かどうかはわかりません。これらの方法の非常に面倒で長い実装になる可能性があります。
代替シリアライゼーション手法
これはOPの質問には答えませんが、取り上げる価値があると思います. JSON、YAML、XML などの ASCII 形式でオブジェクトをシリアル化することを検討してください。そのような場合、シリアライズ可能なインターフェイスを徹底的に再設計しない限り、拡張性はすぐに利用できます。拡張可能なバイナリ プロトコルを探している場合は、 BSON (バイナリ JSON) が適しています。おそらくこれは、Java で実装されていない可能性のあるソフトウェア間でオブジェクトの移植性を提供するための最良の方法です。