目次 | 前の項目 | 次の項目 | Java オブジェクト直列化仕様 バージョン 5.0 |
JavaTM オブジェクトが、直列化を使って状態をファイルに保管したり、BLOB としてデータベースに保管したりする場合、そのデータを読み込むクラスのバージョンがそのデータを書き込んだバージョンと異なる可能性があります。
バージョン管理には、クラスの同一性に関し、いくつかの根本的な問題があります。 たとえば、互換性のある変更とは何か、という問題があります。「互換性のある変更」とは、クラスとその呼び出し元との間の規約に影響を与えない変更です。
この項では、目標、前提条件、および解決策について記述します。この解決策は、変更できるものを制限し、機構を慎重に選択することによって、この問題に対処しようとするものです。
ここで示す解決策では、フィールドの追加やクラスの追加によって展開するクラスを「自動的に」処理する機構を示します。直列化では、バージョン管理は、バージョンごとにクラス固有のメソッドを実装することなく行われます。ストリーム形式は、クラス固有のメソッドを呼び出すことなく処理 (トラバース) されます。
目標は次のとおりです。
前提条件は次のとおりです。
クラスの展開において、非展開クラスによって設定された規約を維持するのは、展開された (あとのバージョンの) クラスの責任です。これは、2 つの形をとります。まず、展開されたクラスは、元のバージョンによって与えられたインタフェースに関する既存の前提条件を壊すことはできません。 それによって、展開されたクラスを元のクラスの代わりに使用することができます。次に、元の (または前の) バージョンと通信するとき、展開されたクラスは、以前のバージョンが非展開クラスの規約を引き続き満たせるだけの、十分で同等な情報を与えなければなりません。
ここで説明した目的のために、各クラスは、そのスーパータイプによって定義されたインタフェースまたは規約を実装し、拡張します。クラスの新しいバージョン、たとえば、foo'
は、foo
のための規約を維持する必要があり、インタフェースを拡張したり、その実装を修正したりすることができます。
直列化を介したオブジェクト間の通信は、それらのインタフェースによって定義される規約には含まれていません。直列化は、実装間の私的なプロトコルです。各実装がそのクライアントによって期待される規約に従うように十分なやりとりをすることは、その実装の責任です。
『JavaTM 言語仕様』の第 13 章に、JavaTM クラスが展開するときのバイナリ互換の説明があります。バイナリ互換の柔軟性のほとんどは、クラス、インタフェース、フィールド、メソッドなどの名前のシンボリック参照を、遅い段階でバインドすることに起因しています。
直列化されたオブジェクトストリームのバージョン管理を設計する場合の基本的な項目を、以下に示します。
writeObject
/readObject
メソッドはクラスの状態を読み込み/書き込みするためのデフォルトの機構に取って代わる。これらのメソッドは、クラスの任意データの読み取りおよび書き込みを実行する。必須データへの書き込みは defaultWriteObject
の呼び出しを介して、必須データの読み取りは defaultReadObject
の呼び出しを介して行われる
ObjectOutputStream
および ObjectInputStream
のサブタイプには、annotateClass
を使って、クラスを識別する独自の情報を含めることができる。たとえば、MarshalOutputStream
はクラスの URL を埋め込んでいる
この概念を使えば、展開するクラスのさまざまなケースに対し、設計上どのように対応するかを説明することができます。これらのケースは、クラスのいずれかのバージョンによって書き込まれたストリームの観点から記述されます。ストリームが同じクラスの同じバージョンで読み込まれた場合には、情報や機能が失われることはありません。このストリームは、元のクラスに関する唯一の情報源です。そのクラス記述は、それが元のクラス記述のサブセットであるかぎり、そのストリームのデータと、再構成されるクラスのバージョンを一致させるのに十分な情報です。
これらの記述は、クラスの以前のバージョンか以後のバージョンを再構成するためにストリームを読み込む、という観点からのものです。RPC システムの用語でいえば、これは「受け取り側が正しくする」システムです。書き込み側は、そのデータをもっとも適した形式で書き込むので、受け取り側は、その情報を解釈して必要な部分を抽出し、入手できない部分を補う必要があります。
クラスに対する互換性のない変更とは、相互運用性の保証が維持できないような変更です。クラスの展開の過程で起こる互換性のない変更には、次のものがあります。
writeObject
メソッドまたは readObject
メソッドを、デフォルトのフィールドデータの書き込みまたは読み込みを行わないように変更したり、前のバージョンが書き込みまたは読み込みを行わなかった場合にその書き込みまたは読み込みを行うように変更する。デフォルトのフィールドデータがストリームにあるかないかは、一貫していなければならない
Serializable
か Externalizable
に変更したり、その反対を行なったりするのは、互換性のない変更である。こうすると、そのストリームに、使用できるクラスの実装と互換性のないデータが入ることになる
Serializable
や Externalizable
を取り除くのは、互換性のない変更である。こうすると、書き込まれたときに、そのクラスの古いバージョンで必要なフィールドが除外されることになる
writeReplace
または readResolve
メソッドをクラスに追加するときに、その動作がクラスの以前のバージョンと互換性がないオブジェクトを作成する場合は、互換性がなくなる
クラスへの互換性のある変更は、次のように処理されます。
writeObject
/readObject
メソッドの追加 - ストリームを読み込むバージョンにこれらのメソッドがある場合、デフォルトの直列化によってストリームに書き込まれた必須データは、通常どおり readObject
によって読み込まれなければならない。このメソッドは、任意データを読み込む前に、まず defaultReadObject
を呼び出す必要がある。writeObject
メソッドは、通常どおり、defaultWriteObject
を呼び出して必須データを書き込まなければならない。 その後、任意データを書き込むことができる
writeObject
/readObject
メソッドの削除 - このストリームを読み込むクラスにこれらのメソッドがないと、必須データはデフォルトの初期化によって読み込まれ、任意データは破棄される
java.io.Serializable
の追加 - これは、型を追加するのと同じことである。ストリームにはこのクラスに対する値がないので、そのフィールドは、デフォルト値に初期化される。直列化不能クラスのサブクラス化をサポートするには、そのクラスのスーパータイプに引数なしのコンストラクタがあり、そのクラス自身がデフォルト値に初期化されなければならない。引数なしのコンストラクタがないと、InvalidClassException
がスローされる
目次 | 前の項目 | 次の項目 | Java オブジェクト直列化仕様 バージョン 5.0 |
Copyright © 2004 Sun Microsystems, Inc. All rights reserved