readResolve 的问题是您需要暂时拥有实例中将被 readResolve 替换的所有状态。这与 final 不能很好地融合。
场景:您想将充满不受欢迎的可变性的东西拉入现代时代。但不会失去序列化形式的兼容性。并且最好不要从 readObject 到 readResolve 增长疯狂的桶连接临时状态。最好不要编写一个成熟的 SerializationProxy,因为它可能会被兼容性要求排除在外。
解决方案:将该临时状态捆绑在一个供应商闭包中:
public class AncientSerializableExample implements Serializable {
// we have two fields in the example to illustrate the
// transient, because we don't want serializability defaults to interfere
final public transient ImmutableList<Integer> immutableInts;
final public transient ImmutableList<String> immutableStrings;
/** funnels the data from readObject to readResolve, transient for obvious reasons,
* we keep all the mutability reaquired to pass data to readResolve contained in here */
private transient Supplier<AncientSerializableExample> deserializationResolver;
public AncientSerializableExample(
List<Integer> ints,
List<String> strings
) {
this.immutableInts = ImmutableList.copyOf(ints);
this.immutableStrings = ImmutableList.copyOf(strings);
}
private void writeObject(final ObjectOutputStream out) throws IOException {
// that ancient serializable form we want to keep untouched clearly wasn't using guava
out.writeObject(new ArrayList<>(immutableInts));
out.writeObject(new ArrayList<>(immutableStrings));
}
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
List<Integer> freshlyReadInts = (List<Integer>) in.readObject();
List<String> freshlyReadStrings = (List<String>) in.readObject();
deserializationResolver = () -> { // our Supplier<AncientSerializableExample> captures the temporary state so conveniently!
deserializationResolver = null; // don't keep the closure, it would prevent the deserialized ArrayLists from getting GCed
return new AncientSerializableExample(
freshlyReadInts,
freshlyReadStrings
);
};
}
/** readResolve won't get any more complicated than this no matter how many fields reality throws at our class,
* and the constructor call is nicely paired with the ObjectInputStream reading and no amount of formatting anarchy
* thrown at the class can change that */
private Object readResolve() throws ObjectStreamException {
return deserializationResolver.get();
}
}