Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I not serialize an inherited non-transient field?

The question

I have a Serializable class (let's call it A) with a non-transient boolean field, and a subclass (B) for which that same field should be transient. How can I do this?

More precisely, I wish the field to be restored to the default boolean value (false) when deserializing B, though I want it to be restored to the correct value when deserializing A. The other fields inherited from A should be restored nonetheless.

Functionnally, A represents an object which is restored between sessions, and B is a particular type of A whose state should be reset on each new session.

Quick code sample:

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
}

Some possible solutions I chose not to retain

  • An easy solution would be to change B extends A to A extends B, make the field transient, and add a writeObject() to A to serialize the field. However, B extends A has a functional meaning, and I am not convinced it would be wise to revert it.

  • I could implement a readObject() method which would overwrite the deserialized value for the field. However, this feels like a dirty solution and I do not wish to use this unless no other choice is left.

  • I tried to write a writeObject() method to emulate a transient field, but it does not work and I cannot tell why. If anybody has a clue, that might be my solution:

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);
        }
    }
}
  • Edit: @rocketboy's solution using shadowing works, but I am uncomfortable with shadowing as it will leave one unused field (A's non-transient field will never be used, while B's transient version will be written and read). It might be a solution though. Do experimented Java developers think this is a clean solution?
public class B extends A {
    // shadow A's field
    private transient boolean field;

    @Override
    public boolean getField() {
        return field;
    }

    @Override
    public void setField(boolean field) {
        this.field = field;
    }
}

The answer

Following @m1o2's advice, I have been able to implement my solution using the Externalizable interface:

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());
    }
}

Please note however that this is applicable because A and B are simple classes. For classes with many fields and a tendency to evolve, this could cost more (except maybe if using some code based on reflection).

like image 213
Chop Avatar asked Jul 30 '13 08:07

Chop


1 Answers

If you don't care about the Superclass fields (all of them) you can use Externalizable interface

like image 123
m1o2 Avatar answered Oct 05 '22 16:10

m1o2