直列化可能クラスについて

Eclipseで「serialVersionUIDが定義されていない」警告が出て、放置しておくのも気持ち悪いので、serialVersionUIDについて調べた。

参考:JDK1.5ドキュメント
参考:APIドキュメント

まず、Serializableについて確認

何者?

直列化可能であることをマーキングするインターフェース

用途は?

SOAPEJBRMIなど)通信、ストリームの読み書きなど様々

Serialize可能なクラスの条件は?

java.io.Serializableを実装していること
デフォルトコンストラクタを定義していること

Serializeされる属性とは?

以下の条件を満たす属性。

  • staticでない
  • transientでない

※Java5でサポートされたEnumとかもSerializeされる。

次に、serialVersionUIDについて確認

目的は?

直列化されたクラスを復元するクラス(通信でデータを受け取る先など)が、自分の知っているバージョンであることを検証するために使う。

どんなふうに使われる?

Deserialize時に、オブジェクトのUIDと、Deserialize処理で用いるクラスのUIDを比較し、バージョンを確認する。
バージョンが違えば、InvalidClassExceptionを発行。

serialVersionUIDはどのように決まる?

A.クラスに属性を定義した場合、これを使用
   値は例。SUNの情報から判断すると、クラス間で重複していても構わないと思われる。

static final long serialVersionUID = 42L;

   いや、別のクラスと重複してはダメだという記述も。ITアーキテクトの記事
B.属性が定義されていない場合は、直列化ランタイムが計算して決める

クラスにserialVersionUIDを定義しないと失敗する状況は?

A.クラスの詳細情報が違う場合
   ※多分、プロパティの増減や型の変更ないが、コードが変更されたケースを指すと思う。
B.Serializeしたクラスと、Deserializeするクラスが別々のコンパイラコンパイルされた場合
   ※コンパイラの実装により依存するとのこと。

定義方法は?

A.Javaツールのserialverを使う。
B.Eclipse3.1以降を使っている場合は、クラスの警告マークで右クリックすると「シリアルバージョンを生成する」メニューが出る。
   ※Eclipse3.1より前のバージョンを使っている場合はプラグインが要るらしい。
C.Javaソース内に手書きする

static final long serialVersionUID = 42L;

※当然、「クラスごとに定義するのは面倒なので、Serializableを実装したクラスを1つ作って継承しちゃえば楽じゃん!」と誰もが考えると思うが、SUN的には「よろしくない」と。用途考えれば確かにそうだけど、けど、最初のリリースはそれでもよいかも。。と思ったりする。

いつserialVersionUIDを変更すべきか?

クラスの互換性を確保できない場合

互換性を確保できないケースとは?

例えば、以下のケース。
つまり、Deserializeするクラスが使用する属性を正しく復元できない場合、「互換性を確保できない」という。
A.Deserializeで使用するクラスのプロパティが多い場合

  • フィールドを削除
  • 非static⇒static,非transient⇒transient

B.プロパティの順番が変わってしまう場合

  • クラスを上下に移動(プロパティの順番が変わるため)

   ※Deserializeで使用するクラスのプロパティが少ない場合は、セットされないだけらしい


他の条件など詳しくは、SUN直列化可能objのバージョン管理を参照のこと。

感想、雑記

serialVersionUIDを定義するとはどういうことか?

通信元と通信先が項目レベルできちんと契約していること。
通信先(受け手)は、受け取ったオブジェクトを用いて何かしらの処理を行うため、処理に必要なデータが欲しい形で格納されていることが必要である。
受け手の作業に必要な情報がきちんと渡せないと、受け手は作業できない。作業に必要な情報がなくなったり変わった場合には明示的に示すことが、serialVersionUIDをアップすることである。
受け手の作業に関係ない項目が追加されても、受け手は作業できる。また、オブジェクトがきちんとカプセル化されているので、内部の作業の仕方が変わっても示す必要がない。これが、実装が変わってもserialVersionUIDを必ずしもアップする必要がない理由である。
現実世界でも同様。AさんがBさんに仕事を依頼するときに、Bさんの仕事に必要な情報をある日から渡せなくなる場合は、ちゃんと連絡しなくては。ただ、Bさんの仕事に関係ない項目が増えた場合は、雑音が増えて作業しにくくなるのでひとこと言っておくけど、契約を変更したりはしない。

これまではどうよ?

実は、serialVersionUIDなんて定義したことはめったになかった。
問題が発生したことも殆どない。(あのときの問題はこれか!と思い当たるフシはあるけど)

今後はどうよ?

今後、WEBサービスが普及し、他のベンダーのサービスをじゃんじゃん利用するようになれば、serialVersionUIDをちゃんと定義しないと問題が発生するケースも増えてくるんだろうな。