【问题标题】:How can I keep -Xcheckinit from interfering with the deserialization of Scala objects?如何防止 -Xcheckinit 干扰 Scala 对象的反序列化?
【发布时间】:2011-04-22 00:40:39
【问题描述】:

当使用 -Xcheckinit 编译器选项并在可序列化类中实现我自己的 readObject 方法时,我无法从 readObject 方法调用在类主体中声明的字段上的任何访问器函数。声明为构造函数参数的字段是可以的。当我尝试访问在类主体中声明的字段时,我得到一个 scala.UninitializedFieldError。

也就是说,下面的代码在readObject方法中的println(y)上失败了,即使在上一行设置了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)
  }
}

为什么?

【问题讨论】:

    标签: serialization scala


    【解决方案1】:

    使用 -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;
    }
    

    请注意,它不会更新构造函数参数的位图,只会更新类主体中声明的字段。它这样做是因为它假定您将调用构造函数并且这些字段将立即初始化。

    然而,在反序列化期间,第一个不可序列化的超类的无参数构造函数被调用(在本例中为 Object),而不是上面显示的单参数构造函数。然后调用 readObject。上面的构造函数永远不会被调用。因此,位图永远不会更新。在调用访问器的任何地方调用访问器都会失败,而不仅仅是在 readObject 方法中。

    要解决此问题,您必须手动更新位图。我选择从 readObject 方法中这样做。我将位图中的所有位都设置为 1,如下所示:

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

    通过将所有位设置为 1,它将适用于所有字段(无论如何最多 32 个字段...超出此范围会发生什么是任何人的猜测)。

    【讨论】:

    • 不错。手动添加超过 32 个惰性值会导致 2 个位图字段。我想初始化检查也是一样的..
    • 哦,对于懒惰的 val,我想必须设计一个稍微复杂的序列化/反序列化例程。
    猜你喜欢
    • 2010-11-07
    • 1970-01-01
    • 1970-01-01
    • 2012-12-03
    • 1970-01-01
    • 2017-08-02
    • 2012-11-23
    • 2022-12-14
    相关资源
    最近更新 更多