当使用-Xcheckinit编译器选项并在可序列化的类中实现我自己的readObject方法时,我无法从readObject方法调用我的类体中声明的字段上的任何访问器函数。声明为构造函数参数的字段是可以的。当我尝试访问类体中声明的字段时,我得到一个scala.UninitializedFieldError。
也就是说,即使在上一行中设置了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)
}
}
为什么?
答案 0 :(得分: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个字段......除此之外发生的事情是任何猜测)。