2012-08-15 63 views
2

不幸的是,問題題目的情況已經發生了好幾年。如何在將類重構爲Java中的接口時支持向後兼容的序列化?

我有一個Id接口,它擴展了Serializable幷包含名字和id號的getter。有一個匹配的IdImpl類實現了接口。但是,在過去的某個時刻,Id的類。還有一個可序列化的容器對象,它具有Id類型的成員字段。這些容器對象已經被序列化到數據庫已有好幾年了,所以容器對象的版本都包含這兩種類型的Id。當試圖反序列化舊對象時,我們得到一個InvalidClassException。如何反序列化包含舊的Id具體類實例的舊容器對象?

披露:到Id接口一對夫婦的其他變化已經取得了多年,但我認爲他們看起來像compatible changes(加入IdImpl場和消氣劑Id爲「字符串idType」;泛型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 */ } 
} 

嘗試反序列化的容器對象時,我們得到的例外是:

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 

回答

6

一般來說,使用的serialVersionUID和車削上課變成一個界面是一個非常困難的過渡到支持。

在這種特定的情況下,我相信你可以支持老Id實施方式:

  • 創建一個類(稱之爲OldId),其中有老的serialVersionUID老實施Id(它應該實現Id)。
  • 創建ObjectInputStream的自定義子類並覆蓋readClassDescriptor()方法。調用父readClassDescriptor和
    • 如果返回的ObjectStreamClass有Id類名和舊的serialVersionUID,返回的ObjectStreamClass新OldId
    • 否則返回給定的描述符
+0

感謝領導我到解決方案。我創建了一個'OldId'類,但是重載'resolveClass'不起作用,因爲'ObjectInputStream'抱怨這個類的名字與流中聲明的不匹配。我結束了[重寫'readClassDescriptor'來代替返回一個不同的'ObjectStreamClass'](http://www.norwinter.com/2009/08/20/refactor-serializable-java-class/)。 – matts 2012-08-15 18:42:53

+0

@matts - sweet,我會爲未來的讀者更新我的答案。 – jtahlborn 2012-08-15 20:38:28

相關問題