Является ли обычным 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