Является ли обычным enum Serializable тоже?

Я понимаю, что Enum является Serializable. Следовательно, это безопасно. (selectedCountry 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);
}

Однако, что, если у меня есть несериализуемые члены в пользовательском классе enum? Например,

Исходные переменные участника-члена enum

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, если у меня есть переменные-члены в моем перечислении?

Ответ 1

Причина, по которой он работает, заключается в том, что процесс сериализации для Enum отличается от процесса сериализации для других классов. Из официальной документации :

1.12 Сериализация констант контировки

Константы континуума сериализуются иначе, чем обычные сериализуемые или внешние объекты. Сериализованная форма константы перечисления состоит исключительно из ее имени; значения поля константы отсутствуют в форме. Чтобы сериализовать константу enum, ObjectOutputStream записывает значение, возвращаемое методом имен имен enum. Для десериализации константы перечисления ObjectInputStream считывает имя константы из потока; десериализованная константа затем получается путем вызова метода java.lang.Enum.valueOf, передающего константный тип перечисления вместе с полученным именем константы в качестве аргументов. Как и другие сериализуемые или внешние объекты, константы перечисления могут функционировать в качестве целей обратных ссылок, появляющихся впоследствии в потоке сериализации.

Это означает, что все ваши настраиваемые поля не будут сериализованы. В вашем случае все работает хорошо, потому что ваш процесс приложения все еще запущен, и вы получаете экземпляр тот же Enum, который вы передали в savedInstanceState.putSerializable.

Но представьте себе ситуацию, когда ваше приложение будет убито, потому что Android не хватает памяти. В следующий раз, когда пользователь откроет приложение, вы получите экземпляр new Enum, и все настраиваемые поля будут потеряны и повторно инициализированы конструктором. Таким образом, изменяемые поля в перечислении всегда эффективно transient.

Ответ 2

По Serializable документация, readObject и writeObject вообще не нужны, поэтому ваш вопрос может быть не полностью правильный.

Serializable является маркерным интерфейсом и не имеет каких-либо методов.

Я отсылаю вас к этому ответу, в котором содержатся дополнительные сведения о реализации Serialization (что объясняет, почему вам не нужны функции записи и чтения).

И, как упоминалось здесь от Dianne Hackborn, Parcelable является гораздо эффективнее для Android.

Если вас особенно интересует Enum, см. ниже в пункте:

1.12 Сериализация констант перечисления

Константы континуума сериализуются иначе, чем обычные сериализуемые или внешние объекты. Сериализованная форма перечисления константа состоит исключительно из ее имени; значения поля константы не представлены в форме. Чтобы сериализовать константу перечисления, ObjectOutputStream записывает значение, возвращаемое константой enum имя метод. Для десериализации константы перечисления, ObjectInputStream читает постоянное имя из потока; тогда десериализованная константа полученных путем вызова метода java.lang.Enum.valueOf, передающего постоянный тип перечисления вместе с полученным постоянным именем как аргументы. Как и другие сериализуемые или внешние объекты, перечисление константы могут функционировать в качестве целей обратных ссылок, появляющихся впоследствии в потоке сериализации.

Процесс, с помощью которого перечислены константные константы, не может быть изменен: любой объект writeObject класса, readObject, readObjectNoData, writeReplace и readResolve, определенные Типы перечислений игнорируются при сериализации и десериализации. Аналогично, любое поле serialPersistentFields или serialVersionUID объявления также игнорируются - все типы перечислений имеют фиксированный serialVersionUID 0L. Документирование сериализуемых полей и данных для перечисления не нужны, так как нет никаких изменений в типе отправленные данные.

Итак, я не думаю, что Enum - правильный выбор для тестирования внутренних несериализуемых классов.

Ответ 3

Сериализация членов перечисления не работает. Поле nonSerializable никогда не сериализуется, как ответил @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