68

Enumはシリアル化可能であることを理解しています。したがって、そうするのは安全です。(selectedCountry is enum Country

顧客メンバー変数のない元の列挙型

public enum Country {
    Australia,
    Austria,
    UnitedState;
}

断片

@Override
public void onActivityCreated (Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    if (savedInstanceState != null) {
        selectedCountry = (Country)savedInstanceState.getSerializable(SELECTED_COUNTRY_KEY);
    }
}

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    savedInstanceState.putSerializable(SELECTED_COUNTRY_KEY, selectedCountry);
}

ただし、カスタム列挙型クラスにシリアル化できないメンバーがある場合はどうなりますか?例えば、

元の列挙型顧客メンバー変数

package org.yccheok;

import org.yccheok.R;

/**
 *
 * @author yccheok
 */
public enum Country {
    Australia(R.drawable.flag_au),
    Austria(R.drawable.flag_at),
    UnitedState(R.drawable.flag_us);

    Country(int icon) {
        this.icon = icon;
        nonSerializableClass = new NonSerializableClass(this.toString());
    }

    public int getIcon() {
        return icon;
    }

    public static class NonSerializableClass {
        public NonSerializableClass(String dummy) { this.dummy = dummy; }
        public String dummy;
    }

    private final int icon;

    public NonSerializableClass nonSerializableClass;
}

私はテストしました。できます。(シリアル化の前後でメンバー変数のすべての値を出力してテストしました。それらは前後で同じです)

しかし、なぜそれが機能するのか分かりませんか?readObject私は適切なとを提供しないので、インターフェースwriteObjectで必要とされSerializableます。

効果的なJavaアイテム75で指摘されているように、カスタムのシリアル化されたフォームの使用を検討してください。列挙型にカスタムメンバー変数がある場合は、独自readObjectのフォームを提供する必要がありますか?writeObject

4

3 に答える 3

111

それが機能する理由は、のシリアルEnum化プロセスが他のクラスのシリアル化プロセスとは異なるためです。公式ドキュメントから:

1.12列挙型定数のシリアル化

列挙型定数は、通常のシリアル化可能または外部化可能なオブジェクトとは異なる方法でシリアル化されます。列挙型定数のシリアル化された形式は、その名前のみで構成されます。定数のフィールド値はフォームに存在しません。列挙型定数をシリアル化するために、ObjectOutputStreamは列挙型定数のnameメソッドによって返される値を書き込みます。列挙型定数を逆シリアル化するために、ObjectInputStreamはストリームから定数名を読み取ります。次に、逆シリアル化された定数は、java.lang.Enum.valueOfメソッドを呼び出し、定数の列挙型と受け取った定数名を引数として渡すことによって取得されます。他のシリアル化可能または外部化可能なオブジェクトと同様に、列挙型定数は、シリアル化ストリームに後で表示される逆参照のターゲットとして機能できます。

つまり、すべてのカスタムフィールドがシリアル化されるわけではありません。あなたの場合、アプリケーションプロセスはまだ実行中であり、に渡したのと同じ Enumインスタンスを取得しているため、すべてが正常に機能しますsavedInstanceState.putSerializable

Androidしかし、十分なメモリがないためにアプリが強制終了される状況を想像してみてください。次にユーザーがアプリを開くと、新しい Enumインスタンスが取得され、すべてのカスタムフィールドが失われ、コンストラクターによって再初期化されます。したがって、列挙型の可変フィールドは常に効果的transientです。

于 2013-03-20T11:28:33.313 に答える
10

シリアル化可能なドキュメントによるreadObjectwriteObject、まったく必要ないため、質問が完全に正しくない可能性があります。

Serializableマーカーインターフェイスであり、メソッドはありません。

シリアル化の実装に関する追加の詳細を提供するこの回答を参照します(これにより、書き込み関数と読み取り関数が必要ない理由が説明されます)。

そして、ここでDianne Hackbornが述べたように、ParcelableはAndroidにとってはるかに効率的です。

列挙型に特に興味がある場合は、以下の段落を参照してください。

1.12列挙型定数のシリアル化

列挙型定数は、通常のシリアル化可能または外部化可能なオブジェクトとは異なる方法でシリアル化されます。列挙型定数のシリアル化された形式は、その名前のみで構成されます。定数のフィールド値はフォームに存在しません。列挙型定数をシリアル化するために、ObjectOutputStreamは列挙型定数のnameメソッドによって返される値を書き込みます。列挙型定数を逆シリアル化するために、ObjectInputStreamはストリームから定数名を読み取ります。次に、逆シリアル化された定数は、java.lang.Enum.valueOfメソッドを呼び出し、定数の列挙型と受け取った定数名を引数として渡すことによって取得されます。他のシリアル化可能または外部化可能なオブジェクトと同様に、列挙型定数は、シリアル化ストリームに後で表示される逆参照のターゲットとして機能できます。

列挙型定数をシリアル化するプロセスはカスタマイズできません。列挙型で定義されたクラス固有のwriteObject、readObject、readObjectNoData、writeReplace、およびreadResolveメソッドは、シリアル化および逆シリアル化中に無視されます。同様に、serialPersistentFieldsまたはserialVersionUIDフィールド宣言も無視されます。すべての列挙型のserialVersionUIDは0Lに固定されています。送信されるデータのタイプに変化がないため、列挙型のシリアル化可能なフィールドとデータを文書化する必要はありません。

Enumしたがって、内部のシリアル化できないクラスの動作をテストするための正しい選択ではないと思います。

于 2013-03-20T10:54:12.910 に答える
4

列挙型メンバーのシリアル化が機能していません。@vmironovが応答したため、非シリアル化可能フィールドがシリアル化されることはありません。これがテストです:

public enum Country {
Australia;

    public static class NonSerializableClass {
       public NonSerializableClass() {}
       public String dummy;
    }

    public NonSerializableClass nonSerializableClass;
}

列挙型をシリアル化ストリームに書き込むコード:

public class SerializationTestWrite {
    public static void main(String[] args) throws Exception{
        FileOutputStream f = new FileOutputStream("tmp");
        ObjectOutput s = new ObjectOutputStream(f);

        Country.Australia.nonSerializableClass = new Country.NonSerializableClass();
        Country.Australia.nonSerializableClass.dummy = "abc";

        s.writeObject(Country.Australia);
        s.flush();

        System.out.println(Country.Australia.nonSerializableClass.dummy);
    }
}    

ダミーフィールドの値を書き込むと、次のようになります。abc

シリアル化ストリームから列挙型を読み取るコード:

public class SerializationTestRead {
    public static void main(String[] args) throws Exception{
        FileInputStream in = new FileInputStream("tmp");
        ObjectInputStream so = new ObjectInputStream(in);
        Country readed = (Country) so.readObject();

        System.out.println(readed.nonSerializableClass);
    }
}

しかし、読むと、フィールドの値は次のようになりnonSerializableClassます。null

于 2013-03-20T11:59:26.573 に答える