在Java中将类重构为接口时,如何支持向后兼容的序列化?
不幸的是,题名中的情况在过去几年中已经出现 我有一个在Java中将类重构为接口时,如何支持向后兼容的序列化?,java,serialization,backwards-compatibility,Java,Serialization,Backwards Compatibility,不幸的是,题名中的情况在过去几年中已经出现 我有一个Id接口,它扩展了Serializable,并包含名称和Id号的getter。有一个匹配的IdImpl类实现该接口。然而,在过去的某个时候,Id是类。还有一个可序列化的容器对象,其成员字段的类型为Id。这些容器对象已经被序列化到数据库好几年了,所以有一些容器对象的版本包含两种类型的Ids。当尝试反序列化旧对象时,我们会得到一个InvalidClassException。如何反序列化包含旧Id具体类实例的旧容器对象 全面披露:多年来,Id接口还做
Id
接口,它扩展了Serializable
,并包含名称和Id号的getter。有一个匹配的IdImpl
类实现该接口。然而,在过去的某个时候,Id
是类。还有一个可序列化的容器对象,其成员字段的类型为Id
。这些容器对象已经被序列化到数据库好几年了,所以有一些容器对象的版本包含两种类型的Id
s。当尝试反序列化旧对象时,我们会得到一个InvalidClassException
。如何反序列化包含旧Id
具体类实例的旧容器对象
全面披露:多年来,Id
接口还做了一些其他更改,但我认为它们看起来像(为IdImpl
添加了一个字段,为“String idType”为Id
添加了一个getter;泛化了Comparable
)。这些变化中的一个也会导致问题吗
这些类看起来像这样:
// 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 */ }
}
一般来说,不使用serialVersionUID并将类转换为接口是一个很难支持的转换
在此特定实例中,我相信您可以通过以下方式支持旧的Id
实现:
- 创建一个类(称之为
),该类具有旧的serialVersionUID和OldId
的旧实现(它应该实现Id
)Id
- 创建ObjectInputStream的自定义子类并重写
方法。调用父readClassDescriptor并readClassDescriptor()
- 如果返回的ObjectStreamClass具有
类名和旧的serialVersionUID,则为新的Id
类返回ObjectStreamClassOldId
- 否则返回给定的描述符
- 如果返回的ObjectStreamClass具有
OldId
类,但是重写resolveClass
无效,因为ObjectInputStream
抱怨类名与流中声明的类名不匹配。结果我却去了。
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