2010-10-06 73 views
4

當使用-Xcheckinit編譯器選項並在可序列化的類中實現我自己的readObject方法時,我無法調用readObject方法在我的類的主體中聲明的字段上的任何訪問器函數。聲明爲構造函數參數的字段是可以的。當我嘗試訪問在類體中聲明的字段時,我得到一個scala.UninitializedFieldError。如何防止-Xcheckinit干擾Scala對象的反序列化?

也就是說,即使在上一行設置了y之後,以下代碼在readObject方法中的println(y)上失敗!

@serializable case class XYPointWithRWAndPrint(var x: Int) { 
    var y = 0 
    @throws(classOf[java.io.IOException]) 
    private def writeObject(out: java.io.ObjectOutputStream) { 
    out.writeInt(x) 
    out.writeInt(y) 
    } 
    @throws(classOf[java.io.IOException]) 
    @throws(classOf[ClassNotFoundException]) 
    private def readObject(in: java.io.ObjectInputStream) { 
    x = in.readInt() 
    println(x) 
    y = in.readInt() 
    println(y) 
    } 
} 

爲什麼?

回答

3

當使用-Xcheckinit編譯器選項時,編譯器會創建一個位圖字段來檢查初始化。

public volatile int bitmap$0; 

在訪問器,編譯器檢查位圖:

public int y(){ 
    if ((this.bitmap$0 & 0x1) != 0){ return this.y; } 
    throw new UninitializedFieldError("Uninitialized field: Test.scala: 2".toString()); 
} 

在構造函數中,編譯器更新位圖:

public XYPointWithRW(int x) { 
    Product.class.$init$(this); 
    this.y = 0; 
    this.bitmap$0 |= 1; 
} 

注意,它不更新位圖對於構造函數參數,僅適用於在類體中聲明的字段。它這樣做是因爲它假定你將調用構造函數,並且這些字段將被立即初始化。

然而,在反序列化過程中,第一個不可序列化的超類的no-arg構造函數被調用(在這種情況下爲Object),而不是上面顯示的one-arg構造函數。然後調用readObject。上面的構造函數永遠不會被調用。因此,位圖從不更新。調用訪問器會失敗它調用的任何地方,而不僅僅是readObject方法。

要解決此問題,您必須手動更新位圖。我選擇了從readObject方法中這樣做。我把所有的位的位圖中的1,像這樣:

getClass.getField("bitmap$0").set(this, -1) 

到所有位設置爲1,它會爲所有字段(多達32個領域的工作,反正......發生什麼事超出了是任何人猜測)。

+0

不錯。手動添加32個以上的惰性val會導致2個位圖字段。我想這對初始化檢查是一樣的。 – axel22 2010-10-06 20:39:03

+0

哦,對於懶惰的vals,我想我們必須設計一個稍微複雜的序列化/反序列化例程。 – axel22 2010-10-06 21:07:44