Serializable subclass of non-serializable parent class

12,943

Solution 1

Is it absolutely necessary to serialize the Location? maybe you could mark it as transient, and obtain it dynamically after deserializing the object. (Anyway, from the documentation ) :

Q: If class A does not implement Serializable but a subclass B implements Serializable, will the fields of class A be serialized when B is serialized?

A: Only the fields of Serializable objects are written out and restored. The object may be restored only if it has a no-arg constructor that will initialize the fields of non-serializable supertypes. If the subclass has access to the state of the superclass it can implement writeObject and readObject to save and restore that state.

So, if the subclass has access to the fields of its non-serializable superclass(es) it can use the writeObject and readObject protocol to implement serialization. Otherwise, there will be fields that won't be possible to serialize.

Solution 2

Looks like Location does not have public/protected no-arg constructor. Such a constructor is needed for making it available for serialization in subclass.

http://download.oracle.com/javase/6/docs/api/java/io/Serializable.html says:

To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype's public, protected, and (if accessible) package fields. The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class's state. It is an error to declare a class Serializable if this is not the case. The error will be detected at runtime.

And same with the words from Serialization specification:

A Serializable class must do the following: ... Have access to the no-arg constructor of its first nonserializable superclass

That would explain why you have problems only in deserialization, because naturally constructor is not called during serialization.

Small example of failing without accessible constructor:

public class A {
    public A(String some) {};
    private A() {} //as protected or public everything would work
}

public class B extends A implements Serializable {
    public B() {
        super("");
    }
    //these doesn't really matter
    //private void writeObject(java.io.ObjectOutputStream out) throws IOException {  }
    //private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { }
}

public class BSerializer {

    public static void main(String ... args) throws Exception {
        B b = new B();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(b);
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        B deserialized = (B) ois.readObject();   //InvalidClassException
    }
}
Share:
12,943
jmc34
Author by

jmc34

Updated on July 19, 2022

Comments

  • jmc34
    jmc34 almost 2 years

    I am hitting a brickwall with serialization of a subclass of Location in android/java

    Location is not serializable. I have a first subclass called FALocation that does not have any instance variables. I have declared it serializable.

    Then I have a second class called Waypoint that looks like this:

    public class Waypoint extends FALocation implements Serializable {
    
        /**
         * 
         */
        private static final long serialVersionUID = 1L;
    
        /* Class variables *******************************************************/
        private static int CLASS_VERSION=1; //Used to version parcels
    
        /* Instance variables ****************************************************/
        private transient String type=DataHelper.PT_TYPE_US;
        private transient String country; 
        private transient String name=null;
        private transient String description=null;
        private transient int elevation = 0;
        private transient int population = 0; // Afterthought, added to match the DB structure
    
        /* Constructors **********************************************************/    
        public Waypoint() {
            super();
        }
    
        public Waypoint(double lat, double lon, String name, String description) {
            super(lat, lon);
            this.setName(name);
            this.setDescription(description);
        }
    
        public Waypoint(Location l) {
            super(l);
        }
    
        public Waypoint(String provider) {
            super(provider);
        }
    
    
        /* Implementing serializable */
        private void writeObject(java.io.ObjectOutputStream out) throws IOException {
            Log.v("DroidFA", "Serialising \"%s\" (v%d).", Waypoint.class.getSimpleName(), CLASS_VERSION);
            out.writeInt(CLASS_VERSION);
    
            out.writeObject(type);
            out.writeObject(country);
            out.writeObject(name);
            out.writeObject(description);
            out.writeInt(elevation);
            out.writeInt(population);
        }
    
        private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
    
            int serialClassVersion = in.readInt();
            Log.v("DroidFA", "Deserialising \"%s\" (v%d).", Waypoint.class.getSimpleName(),serialClassVersion);
    
            type = (String) in.readObject();
            country = (String) in.readObject();
            name = (String) in.readObject();
            description = (String) in.readObject();
            elevation = in.readInt();
            population = in.readInt();
        }
    }
    

    Serialization works fine.

    Deseriamization produces the followwing exception (the leg object contains a waypoint).:

    10-05 13:50:35.259: WARN/System.err(7867): java.io.InvalidClassException: android.location.Location; IllegalAccessException
    10-05 13:50:35.267: WARN/System.err(7867):     at java.io.ObjectInputStream.resolveConstructorClass(ObjectInputStream.java:2010)
    10-05 13:50:35.267: WARN/System.err(7867):     at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:2095)
    10-05 13:50:35.267: WARN/System.err(7867):     at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:929)
    10-05 13:50:35.267: WARN/System.err(7867):     at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2285)
    10-05 13:50:35.278: WARN/System.err(7867):     at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2240)
    10-05 13:50:35.278: WARN/System.err(7867):     at com.droidfa.navigation.Leg.readObject(Leg.java:262)
    .../...
    
  • bugs_
    bugs_ over 12 years
    android.location.Location; IllegalAccessException is Thrown when a program attempts to access a field or method which is not accessible from the location where the reference is made.
  • jmc34
    jmc34 over 12 years
    Location indeed does not have a no arg public constructor. But where should I go from there?
  • Mikko Maunu
    Mikko Maunu over 12 years
    Unfortunately then you cannot serialize it directly. You cannot do anything more than save state somewhere else, take saved state and construct new Location (or whatever extends it).
  • Mikko Maunu
    Mikko Maunu over 12 years
    @bugs_ thats true, but it has nothing to do with this case. There is no IllegalAccessException thrown, "IllegalAccessException" was msg argument to the constructor of InvalidClassException which was created and thrown from resolveConstructorClass in ObjectInputStream class.
  • jmc34
    jmc34 over 12 years
    Ok, I can serialize the Location fields from the subclass, but the issue is that it tries to deserialize the Location anyway. Is there anyway I can stop that happening from the subclass ?
  • jmc34
    jmc34 over 12 years
    Ok, I have "pseudo" serielized my Location subclass from the enclosing object. Solved the problem. Thanks.
  • stack_ved
    stack_ved almost 10 years
    " I have "pseudo" serielized my Location subclass from the enclosing object." Hi i am facing the same, could you please elaborate. WHat does Pseudo mean here?