Вопрос
У меня есть класс Serializable (пусть он называется A) с непереходным булевым полем и подкласс (B), для которого это же поле должно быть временным. Как я могу это сделать?
Точнее, я хочу, чтобы поле было восстановлено по умолчанию по булевскому значению (false
) при десериализации B, хотя я хочу, чтобы оно было восстановлено до правильного значения при десериализации A. Другие поля, унаследованные от A, должны быть но тем не менее.
Функционально, A представляет объект, который восстанавливается между сеансами, а B - конкретный тип A, состояние которого должно быть reset в каждом новом сеансе.
Пример быстрого кода:
public class A implements java.io.Serializable {
private String label;
// non-transient
private boolean field;
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public boolean isField() {
return field;
}
public void setField(boolean field) {
this.field = field;
}
}
public class B extends A {
// field should be transient for this class
// label should remain non-transient
}
Некоторые возможные решения, которые я решил не сохранять
-
Легким решением было бы изменить
B extends A
наA extends B
, сделать поле временным и добавить awriteObject()
в для сериализации поля. ОднакоB extends A
имеет функциональное значение, и я не уверен, что было бы разумно его вернуть. -
Я мог бы реализовать метод
readObject()
, который заменил бы десериализованное значение для поля. Однако это похоже на грязное решение, и я не хочу использовать его, если не останется другого выбора. -
Я попытался написать метод
writeObject()
для эмуляции переходного поля, но он не работает, и я не могу сказать почему. Если у кого-то есть ключ, это может быть моим решением:
public class B extends A {
private void writeObject(ObjectOutputStream out) throws IOException {
// save current state
boolean field = isField();
// synchronized to make sure this instance is not interrogated
// while changed for serialization
synchronized (this) {
// emulate transient state and serialize
setField(false);
out.defaultWriteObject();
// restore state
setField(field);
}
}
}
- Изменить: решение @rocketboy с использованием теневых работ, но мне неудобно затенять, поскольку оно оставит одно неиспользуемое поле (поле без переходных процессов никогда не будет использоваться, а B-переходная версия будет записана и прочитана). Это может быть решение. Экспериментировали разработчики Java, что это чистое решение?
public class B extends A {
// shadow A field
private transient boolean field;
@Override
public boolean getField() {
return field;
}
@Override
public void setField(boolean field) {
this.field = field;
}
}
Ответ
Следуя советам @m1o2, я смог реализовать свое решение, используя интерфейс Externalizable:
public class B extends A implements java.io.Externalizable {
// Do not forget to have a public no-arg constructor
// for Serialization to work
public B() {
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
// Write only the fields I am interested in
out.writeObject(getLabel());
}
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
// Read the serialized fields IN THE ORDER THEY WERE WRITTEN
setLabel((String) in.readObject());
}
}
Обратите внимание, что это применимо, так как A и B - простые классы. Для классов со многими полями и тенденцией к эволюции это может стоить больше (за исключением, может быть, использования кода, основанного на отражении).