Пользовательское перечисление тоже сериализуемо?
Я понимаю, что Enum
сериализуемо. Следовательно, это безопасно. (Выбранная страна - это 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
интерфейс.
Как указано в разделе Effective Java Пункт 75: Рассмотрите возможность использования пользовательской сериализованной формы, нужно ли мне предоставлять свою собственную readObject
и writeObject
, если у меня есть пользовательские переменные-члены в моем перечислении?
Переведено автоматически
Ответ 1
Причина, по которой это работает, заключается в том, что процесс сериализации для Enum
's отличается от процесса сериализации для других классов. Из официальной документации:
1.12 Сериализация констант Enum
Константы Enum сериализуются иначе, чем обычные сериализуемые или экстернализуемые объекты. Сериализованная форма константы enum состоит исключительно из ее имени; значения полей константы в форме отсутствуют. Чтобы сериализовать константу enum, ObjectOutputStream записывает значение, возвращаемое методом name константы enum. Чтобы десериализовать константу enum, ObjectInputStream считывает имя константы из потока; затем десериализованная константа получается путем вызова метода java.lang.Enum.valueOf, передавая тип enum константы вместе с полученным именем константы в качестве аргументов. Подобно другим сериализуемым или экстернализуемым объектам, константы enum могут функционировать как целевые объекты обратных ссылок, появляющихся впоследствии в потоке сериализации.
Это означает, что все ваши пользовательские поля не будут сериализованы. В вашем случае все работает хорошо, потому что процесс вашего приложения все еще запущен, и вы получаете тот же Enum
экземпляр, которому вы передали savedInstanceState.putSerializable
.
Но представьте ситуацию, когда ваше приложение будет убито, потому что Android
у него недостаточно памяти. В следующий раз, когда пользователь откроет приложение, вы получите новый Enum
экземпляр, а все пользовательские поля будут потеряны и повторно инициализированы конструктором. Таким образом, изменяемые поля в перечислении всегда эффективны transient
.
Ответ 2
Согласно сериализуемой документации, readObject
и writeObject
вообще не нужны, поэтому ваш вопрос может быть не совсем корректным.
Serializable
это маркерный интерфейс и не имеет никаких методов.
Я отсылаю вас к этому ответу, который предоставляет дополнительные сведения о реализации сериализации (что объясняет, почему вам не обязательно нужны функции записи и чтения).
И, как упомянула здесь Дайанн Хакборн, Parcelable намного эффективнее для Android.
Если вас особенно интересует Enum, обратитесь к нижеприведенному параграфу:
1.12 Сериализация констант Enum
Константы Enum сериализуются иначе, чем обычные сериализуемые или экстернализуемые объекты. Сериализованная форма константы enum состоит исключительно из ее имени; значения полей константы в форме отсутствуют. Чтобы сериализовать константу enum, ObjectOutputStream записывает значение, возвращаемое методом name константы enum. Чтобы десериализовать константу enum, ObjectInputStream считывает имя константы из потока; затем десериализованная константа получается путем вызова метода java.lang.Enum.valueOf, передавая тип enum константы вместе с полученным именем константы в качестве аргументов. Подобно другим сериализуемым или экстернализуемым объектам, константы enum могут функционировать как целевые объекты обратных ссылок, появляющихся впоследствии в потоке сериализации.
Процесс, с помощью которого сериализуются константы enum, нельзя настроить: любые методы writeObject, readObject, readObjectNoData, writeReplace и readResolve для конкретного класса, определенные типами enum, игнорируются во время сериализации и десериализации. Аналогично, любые объявления полей serialPersistentFields или serialVersionUID также игнорируются - все типы enum имеют фиксированный serialVersionUID, равный 0L. Документирование сериализуемых полей и данных для типов перечислений не требуется, поскольку тип отправляемых данных не меняется.
Итак, я не думаю, что Enum
является правильным выбором для тестирования работы внутренних несериализуемых классов.
Ответ 3
Сериализация элементов enum не работает. Несериализуемое поле никогда не сериализуется, как ответил @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