Reputation: 6897
Unfortunately, the situation in the question title has already happened several years in the past.
I have an Id
interface which extends Serializable
and contains getters for a name and id number. There is a matching IdImpl
class which implements the interface. However, at some point in the past, Id
was the class. There is also a serializable container object which has member fields of type Id
. These container objects have been being serialized to a database for several years, so there are versions of the container object out there containing both types of Id
s. When attempting to deserialize the old objects, we get an InvalidClassException
. How can I deserialize the old container objects which contain the old Id
concrete class instances?
Full disclosure: a couple of other changes to the Id
interface have been made over the years, but I thought they looked like compatible changes (added a field to IdImpl
and a getter to Id
for "String idType"; generified Comparable
). Could one of these changes be causing the problem too?
The classes look something like this:
// current Id interface
public interface Id extends Serializable, Comparable<Id> {
String getName();
int getIdNumber();
}
// current Id implementation
public class IdImpl implements Id {
private static final long serialVersionUID = 10329865109284L;
private String name;
private int idNumber;
IdImpl(String name, int idNumber) { this.name = name; this.idNumber = idNumber; }
@Override public String getName() { return name; }
@Override public int getIdNumber() { return idNumber; }
@Override public int compareTo(Id id) { /* some code here */ }
}
// the container object
public ContainerForm implements Serializable {
private static final long serialVersionUID = -3294779665912049275L;
private String someField;
private Id user;
private String someOtherField;
// getters and setters
}
// this is what the _old_ Id concrete class looked like
// (from source control history; not in current project)
public class Id implements Serializable, Comparable {
// never had a serialVersionUID
private String name;
private int idNumber;
Id(String name, int idNumber) { this.name = name; this.idNumber = idNumber; }
public String getName() { return name; }
public int getIdNumber() { return idNumber; }
public int compareTo(Object id) { /* some code here */ }
}
The exception we get when trying to deserialize container objects is:
java.io.InvalidClassException: mypkg.people.Id; local class incompatible: stream classdesc serialVersionUID = -6494896316839337071, local class serialVersionUID = -869017349143998644
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:562)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1583)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1496)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1732)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1947)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1871)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1753)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
at mypkg.forms.FormFactory.getForm(FormFactory.java:3115)
... 34 more
Upvotes: 5
Views: 1937
Reputation: 53694
As a general rule, not using serialVersionUID and turning a class into an interface is a very difficult transition to support.
In this specific instance, i believe you could support the old Id
implementation by:
OldId
) which has the old serialVersionUID and old implementation of Id
(it should implement Id
).readClassDescriptor()
method. call the parent readClassDescriptor and
Id
class name and the old serialVersionUID, return the ObjectStreamClass for the new OldId
classUpvotes: 6