Как я могу не сериализовать унаследованное поле, не прошедшее переход?

Вопрос

У меня есть класс 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, сделать поле временным и добавить a writeObject() в для сериализации поля. Однако 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 - простые классы. Для классов со многими полями и тенденцией к эволюции это может стоить больше (за исключением, может быть, использования кода, основанного на отражении).

Ответ 1

Если вам не нужны поля Суперкласса (все они), вы можете использовать Externalizable интерфейс