Should an abstract class have a serialVersionUID

19,902

Solution 1

The serialVersionUID is provided to determine compatibility between a deseralized object and the current version of the class. As such, it isn't really necessary in the first version of a class, or in this case, in an abstract base class. You'll never have an instance of that abstract class to serialize/deserialize, so it doesn't need a serialVersionUID.

(Of course, it does generate a compiler warning, which you want to get rid of, right?)

It turns out james' comment is correct. The serialVersionUID of an abstract base class does get propagated to subclasses. In light of that, you do need the serialVersionUID in your base class.

The code to test:

import java.io.Serializable;

public abstract class Base implements Serializable {

    private int x = 0;
    private int y = 0;

    private static final long serialVersionUID = 1L;

    public String toString()
    {
        return "Base X: " + x + ", Base Y: " + y;
    }
}



import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Sub extends Base {

    private int z = 0;

    private static final long serialVersionUID = 1000L;

    public String toString()
    {
        return super.toString() + ", Sub Z: " + z;
    }

    public static void main(String[] args)
    {
        Sub s1 = new Sub();
        System.out.println( s1.toString() );

        // Serialize the object and save it to a file
        try {
            FileOutputStream fout = new FileOutputStream("object.dat");
            ObjectOutputStream oos = new ObjectOutputStream(fout);
            oos.writeObject( s1 );
            oos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        Sub s2 = null;
        // Load the file and deserialize the object
        try {
            FileInputStream fin = new FileInputStream("object.dat");
            ObjectInputStream ois = new ObjectInputStream(fin);
            s2 = (Sub) ois.readObject();
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println( s2.toString() );
    }
}

Run the main in Sub once to get it to create and save an object. Then change the serialVersionUID in the Base class, comment out the lines in main that save the object (so it doesn't save it again, you just want to load the old one), and run it again. This will result in an exception

java.io.InvalidClassException: Base; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2

Solution 2

Yes, in general, for the same reason that any other class needs a serial id - to avoid one being generated for it. Basically any class (not interface) that implements serializable should define serial version id or you risk de-serialization errors when the same .class compile is not in the server and client JVMs.

There are other options if you are trying to do something fancy. I'm not sure what you mean by "it is the intention of the sub classes...". Are you going to write custom serialization methods (eg. writeObject, readObject)? If so there are other options for dealing with a super class.

see: http://java.sun.com/javase/6/docs/api/java/io/Serializable.html

HTH Tom

Solution 3

Actually, pointing out of Tom's link if missing serialVersionID is actually calculated by serialization runtime i.e. not during compilation

If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUID value for that class based on various aspects of the class...

This makes things even more complicated having different versions of JRE.

Solution 4

Conceptually, the serialized data look like this:

subClassData(className + version + fieldNames + fieldValues)
parentClassData(className + version + fieldNames + fieldValues)
... (up to the first parent, that implements Serializable)

So when you deserialize, a mismatch in the version in any of the classes in the hierarchy causes the deserialization to fail. Nothing is stored for interfaces, so there's no need to specify version for them.

So the answer is: yes, you do need to provide serialVersionUID in the base abstract class, even if it does not have fields: className + version is still stored.

Also note the following:

  1. If a class does not have a field that is encountered in the serialized data (a removed field), it is ignored.
  2. If a class has a field that is not present in the serialized data (a new field), it's set to 0/false/null. It's not set to the default value as one might expect.
  3. If a field changes type, the deserialized value must be assignable to the new type. E.g. if you had an Object field with String value, changing the field type to String will succeed, but changing it to Integer won't. However, changing field from int to long won't work, even though you can assign int value to long variable.
  4. If a subclass no longer extends the parent class which it extends in the serialized data, it is ignored (as in case 1).
  5. If a subclass now extends a class that is not found in the serialized data, parent class fields are restored with 0/false/null value (as in case 2).

In simple words: you can reorder fields, add and remove them, even change the class hierarchy. You should not rename fields or classes (it won't fail, but it will be handled as if that field was removed and added). You cannot change the type of fields with primitive type, and you can change reference type fields provided the new type is assignable from all serialized values.

Note: if the base class doesn't implement Serializable and only the subclass does, then fields from the base class will behave as transient.

Share:
19,902
Yishai
Author by

Yishai

Updated on July 01, 2022

Comments

  • Yishai
    Yishai almost 2 years

    In java, if a class implements Serializable but is abstract, should it have a serialVersionUID long declared, or do the subclasses only require that?

    In this case it is indeed the intention that all the sub classes deal with serialization as the purpose of the type is to be used in RMI calls.

  • Ryan Anderson
    Ryan Anderson almost 15 years
    Good answer... @SuppressWarnings("serial") will suppress the warning message
  • Bill the Lizard
    Bill the Lizard almost 15 years
    @Ryan: Thanks, but I typically treat warnings like errors and deal with them directly.
  • Bill the Lizard
    Bill the Lizard almost 15 years
    ...but I understand that not everyone is as dogmatic about it as I am, so your comment is appreciated.
  • james
    james almost 15 years
    actually, this is incorrect. during deserialization, the serialversionuid of all the classes in the inheritance chain is taken into account, thus the lack of one on an abstract class could be problematic. i actually encountered this issue.
  • Xairoo
    Xairoo almost 15 years
    It should also be present in the first version of the class, since recompiling it with a different compiler may produce a different default serialVersionUID. Thus rendering a newly compiled version of the class (with no code changes) incompatible with the old. Check the note java.sun.com/j2se/1.5.0/docs/guide/serialization/spec/…
  • Bill the Lizard
    Bill the Lizard almost 15 years
    @Robin: Strongly recommended, but not strictly necessary. I always include it to get rid of the warning, which I'm sure is there to avoid the problem you're pointing out.
  • Bill the Lizard
    Bill the Lizard almost 15 years
    @james: I'm still trying to figure out how I'm going to test that the serialVersionUID of a base class, a static member, is taken into account during the serialization of an extending class.
  • Yishai
    Yishai almost 15 years
    I originally accepted this answer, but given james' comment, looks like a unit test is in order to find out, so I'll have to investigate.
  • Yishai
    Yishai almost 15 years
    @Bill, Thanks for the research!
  • vsingh
    vsingh over 10 years
    I voted the answer up and we have concrete example of child class the serialVersionUID of an abstract base class does get propagated to subclasses But from java docs download.java.net/jdk8/docs/api/java/io/Serializable.html you will have to declare serialVersionUID explicitly. "It is also strongly advised that explicit serialVersionUID declarations use the private modifier where possible, since such declarations apply only to the immediately declaring class--serialVersionUID fields are not useful as inherited members."