Bits of Java (トップ)

目次  前へ  次へ

readResolve メソッドの実装

直列化される Serializable あるいは Externalizable インターフェイスを実装しているクラスに readResolve メソッドを定義している場合には復元の最終段階として復元されたオブジェクトの readResolve メソッドが呼び出されその戻り値が最終的に復元されたオブジェクトとなります。 readResolve メソッドは Java の直列化フレームワークにおいて Serializable あるいは Externalizable インターフェイスを実装しているクラスが定義している場合に意味のあるメソッドであり、インターフェイスやどこかのクラスに定義されているメソッドをオーバーライドしているわけではありません。 readResolve メソッドの定義はアクセス修飾子は任意で以下のようになります。

ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException
writeObject や readObject メソッドと違って readResolve メソッドは直列化されたクラスに 定義されている場合のみ復元時に呼び出されます。 Serializable であるスーパークラスが存在している場合でそのスーパークラスに readResolve メソッドが定義されていても呼び出されることはありません。
Object -> S -> Foo
上の継承関係で S, Foo クラスのクラス宣言は
public class S implements java.io.Serializable
public class Foo extends S
となっていて Foo と S の両方に readResolve メソッドが定義されているとします。 ここで Foo を直列化、復元する場合には復元時に呼び出される readResolve メソッドは Foo#readResolve メソッドであり S#readResolve メソッドが呼び出されることはありません。 たとえ Foo が readResolve メソッドを定義していない場合でも S#readResolve メソッドは呼び出されません。
直列化された Foo オブジェクトの復元の際に (もしも readResolve メソッドが定義されていなければ復元が完了するという状態で) Foo オブジェクト自身の readResolve メソッドが実行されます。 実装内容が
return this;
の1行の場合には readResolve メソッドが定義されていないのと同じ結果になります。 Foo オブジェクト自身の状態を変更してから this を返す実装やまったく別の Foo オブジェクトを作成して返すことも可能です。

下のプログラムの LineEventType は Effective Java (書籍 ピアソンエデュケーション刊)で紹介されているタイプセーフ enum です。 LineEvent というイベントのタイプを int 型の定数ではなくオブジェクトで 表す為のクラスです。

if (lineEvent.getType() == LineEventType.LINE_ADDED)
のように int 型の定数と同じように使えます。 同じ VM 上では LineEventType オブジェクトは LineEventType クラスの public static final で定義されているオブジェクトしか存在しません。 しかしデフォルトの直列化、復元では新たな LineEventType オブジェクトを生成してしまい、参照による比較が正しくできなくなってしまいます。 LineEventType では同じ VM 上で直列化、復元しても元の参照を維持できるように readResolve メソッドを定義しています。

Java 1.5 からは enum が導入されているのでこのようなクラスを作る必要がなくなりました。 Java 1.5 の enum においては ObjectInputStream が enum に対応するように修正されているので下のプログラムのような readResolve メソッドの定義を行わなくても安全に直列化、復元できます。

SerializationSampleの実行結果は

(LineEventType.LINE_REMOVED == deserializedObject) -> true
となります。

/************************ LineEventType.java ************************/
import java.io.Serializable;
import java.io.ObjectStreamException;
import java.io.InvalidObjectException;

public final class LineEventType implements Serializable {

    /** 行番号を表示する行が1行増加 */
    public static final LineEventType LINE_ADDED;

    /** 行番号を表示している行が1行削除 */
    public static final LineEventType LINE_REMOVED;

    /** 行番号を表示しない折返し行が1行増加 */
    public static final LineEventType UNCOUNT_LINE_ADDED;

    /** 行番号を表示していない折返し行が1行削除 */
    public static final LineEventType UNCOUNT_LINE_REMOVED;

    /** 行番号を表示しない折返し行を行番号を表示する行に変更 */
    public static final LineEventType REPLACE_TO_NUMBER_LINE;

    /** 行番号を表示している行を行番号を表示しない折返し行に変更 */
    public static final LineEventType REPLACE_TO_UNCOUNT_LINE;

    static {
        LINE_ADDED              = new LineEventType("LINE_ADDED");
        LINE_REMOVED            = new LineEventType("LINE_REMOVED");
        UNCOUNT_LINE_ADDED      = new LineEventType("UNCOUNT_LINE_ADDED");
        UNCOUNT_LINE_REMOVED    = new LineEventType("UNCOUNT_LINE_REMOVED");
        REPLACE_TO_NUMBER_LINE  = new LineEventType("REPLACE_TO_NUMBER_LINE");
        REPLACE_TO_UNCOUNT_LINE = new LineEventType("REPLACE_TO_UNCOUNT_LINE");
    }

    private String type;

    private LineEventType(String type) {
        this.type = type;
    }
    public String toString() {
        return type;
    }
    private Object readResolve() throws ObjectStreamException {
        if (type.equals(LINE_ADDED.type)) {
            return LINE_ADDED;

        } else if (type.equals(LINE_REMOVED.type)) {
            return LINE_REMOVED;

        } else if (type.equals(UNCOUNT_LINE_ADDED.type)) {
            return UNCOUNT_LINE_ADDED;

        } else if (type.equals(UNCOUNT_LINE_REMOVED.type)) {
            return UNCOUNT_LINE_REMOVED;

        } else if (type.equals(REPLACE_TO_NUMBER_LINE.type)) {
            return REPLACE_TO_NUMBER_LINE;

        } else if (type.equals(REPLACE_TO_UNCOUNT_LINE.type)) {
            return REPLACE_TO_UNCOUNT_LINE;

        }
        throw new InvalidObjectException("Invalid type:" + type);
    }

}

/********************* SerializationSample.java *********************/
import java.io.*;

public class SerializationSample {
    public static void main(String[] args) throws Exception {
        ObjectOutputStream out = null;
        ObjectInputStream in = null;
        try {
            out = new ObjectOutputStream(
                      new BufferedOutputStream(
                          new FileOutputStream("data")));
            out.writeObject(LineEventType.LINE_REMOVED);
            out.close();
            out = null;

            in = new ObjectInputStream(
                     new BufferedInputStream(
                         new FileInputStream("data")));

            Object deserializedObject = in.readObject();
            System.out.println(
                "(LineEventType.LINE_REMOVED == deserializedObject) -> " +
                (LineEventType.LINE_REMOVED == deserializedObject));
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {}
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {}
            }
        }
    }
}