異なるバージョン間でもシリアライズ・デシリアライズする

一般的にクラスのバージョン(構造)が異なる場合デシリアライズできませんが、以下の様なことをしておくとシリアライズ、デシリアライズが可能です。

  • private static final long serialVersionUIDを定義
  • writeObject/readObjectを実装する。

serialVersionUIDは知っていましたが、writeObject/readObjectは知らなかった。
writeObject/readObjectは特にSerializableインターフェース等に定義されているわけではないが、Serializableインターフェースをインプリメントすると使用されることになるちょっと不思議なメソッド
writeObject メソッドが定義されていればその writeObject メソッドを呼び出し、そうでなければ ObjectOutputStreame の defaultWriteObject が使用されるらしい。

public class Rectangle extends Figure implements Serializable {
    public Rectangle(int x, int y, int width, int height) {
        setValues(x, y);
        width_ = width;
        height_ = height;
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject(); // Rectangleをシリアライズ
        out.writeInt(getX());     // Figureのデータをシリアライズ
        out.writeInt(getY());     //
    }

    private void readObject(ObjectInputStream in) thows IOException, ClassNotFoundException {
        in.defaultReadObject(); // Figureは引数なしコンストラクタで初期化
                                // Rectangleをデシリアライズ(復元)
        int x = in.readInt();   // Figureのデータをデシリアライズ(復元)
        int y = in.readInt();   //
        setValues(x, y);        // Figureに復元したデータを設定
    }

    private int width_;
    private int height_;
}
http://www.asahi-net.or.jp/~DP8T-ASM/java/tips/Serializable.html

そして、バージョンの異なる(定義の異なる)クラスをシリアライズ・デシリアライズした場合
フィールドの値は以下のようになります。

元にある 元にない
先にある 値が引き継がれる Null
先にない 無視 無視

NULLになるところの場合変数宣言時に初期化をしていてもNULLもしくは0になります。
プリミティブなものは初期値(intなら0)になります。

普段何気なく使っているものをどれだけ知らないか、改めて認識。