我有以下两个类使用 Effective Java 一书中的序列化代理模式。我想我因为循环依赖而遇到麻烦,我怎么能解决它呢?
public class Symbol implements Serializable {
private static final long serialVersionUID = 23829245030202L;
private final String symbol;
private final float confidence;
private final boolean dropcap;
private final boolean subscript;
private final boolean superscript;
private final Rectangle boundingBox;
private final Rectangle baseline;
private final List<SymbolChoice> symbolChoices;
private Word parentWord;
private Symbol(final String symbol, final float confidence, final boolean dropcap, final boolean subscript, final boolean superscript, final Rectangle boundingBox, final Rectangle baseline, final List<SymbolChoice> symbolChoices) {
this.symbol = Objects.requireNonNull(symbol, "symbol");
this.confidence = confidence;
this.dropcap = dropcap;
this.subscript = subscript;
this.superscript = superscript;
this.boundingBox = Objects.requireNonNull(boundingBox, "boundingBox");
this.baseline = Objects.requireNonNull(baseline, "baseline");
this.symbolChoices = Objects.requireNonNull(symbolChoices, "symbolChoices");
}
private void setParentWord(final Word parentWord) {
this.parentWord = Objects.requireNonNull(parentWord, "parentWord");
}
public String getSymbol() {
return symbol;
}
public float getConfidence() {
return confidence;
}
public boolean isDropcap() {
return dropcap;
}
public boolean isSubscript() {
return subscript;
}
public boolean isSuperscript() {
return superscript;
}
public Rectangle getBoundingBox() {
return boundingBox;
}
public Rectangle getBaseline() {
return baseline;
}
public List<SymbolChoice> getSymbolChoices() {
return symbolChoices;
}
public Word getParentWord() {
return parentWord;
}
public static class SymbolBuilder {
private final String symbol;
private final float confidence;
private final boolean dropcap;
private final boolean subscript;
private final boolean superscript;
private final Rectangle boundingBox;
private final Rectangle baseline;
private final List<SymbolChoice> symbolChoices = new ArrayList<SymbolChoice>();
public SymbolBuilder(final String symbol, final float confidence, final boolean dropcap, final boolean subscript, final boolean superscript, final Rectangle boundingBox, final Rectangle baseline) {
this.symbol = symbol;
this.confidence = confidence;
this.dropcap = dropcap;
this.subscript = subscript;
this.superscript = superscript;
this.boundingBox = boundingBox;
this.baseline = baseline;
}
public SymbolBuilder addSymbolChoice(final SymbolChoice symbolChoice) {
symbolChoices.add(Objects.requireNonNull(symbolChoice, "symbolChoice"));
return this;
}
public Symbol build() {
return new Symbol(symbol, confidence, dropcap, subscript, superscript, boundingBox, baseline, symbolChoices);
}
}
private Object writeReplace() {
return new SerializationProxy(this);
}
private void readObject(final ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
private static class SerializationProxy implements Serializable {
private static final long serialVersionUID = 49545459839839843L;
private final String symbol;
private final float confidence;
private final boolean dropcap;
private final boolean subscript;
private final boolean superscript;
private final Rectangle boundingBox;
private final Rectangle baseline;
private final List<SymbolChoice> symbolChoices;
private final Word parentWord;
private SerializationProxy(final Symbol symbol) {
this.symbol = symbol.symbol;
this.confidence = symbol.confidence;
this.dropcap = symbol.dropcap;
this.subscript = symbol.subscript;
this.superscript = symbol.superscript;
this.symbolChoices = symbol.symbolChoices;
this.boundingBox = symbol.boundingBox;
this.baseline = symbol.baseline;
this.parentWord = symbol.parentWord;
}
private Object readResolve() {
Symbol localSymbol = new Symbol(symbol, confidence, dropcap, subscript, superscript, boundingBox, baseline, symbolChoices);
localSymbol.setParentWord(parentWord);
return localSymbol;
}
}
}
public class Word implements Serializable {
private static final long serialVersionUID = 9084633893292833L;
private final String word;
private final float confidence;
private final FontAttributes fontAttributes;
private final boolean fromDictionary;
private final boolean numeric;
private final Rectangle boundingBox;
private final Rectangle baseline;
private final List<Symbol> symbols;
private Textline parentTextline;
private Word(final String word, final float confidence, final FontAttributes fontAttributes, final boolean fromDictionary, final boolean numeric, final Rectangle boundingBox, final Rectangle baseline, final List<Symbol> symbols) {
this.word = Objects.requireNonNull(word, "word");
this.confidence = confidence;
this.fontAttributes = Objects.requireNonNull(fontAttributes, "fontAttributes");
this.fromDictionary = fromDictionary;
this.numeric = numeric;
this.boundingBox = Objects.requireNonNull(boundingBox, "boundingBox");
this.baseline = Objects.requireNonNull(baseline, "baseline");
this.symbols = Objects.requireNonNull(symbols, "symbols");
}
private void setParentTextline(final Textline parentTextline) {
this.parentTextline = Objects.requireNonNull(parentTextline, "parentTextline");
}
public String getWord() {
return word;
}
public float getConfidence() {
return confidence;
}
public FontAttributes getFontAttributes() {
return fontAttributes;
}
public boolean isFromDictionary() {
return fromDictionary;
}
public boolean isNumeric() {
return numeric;
}
public Rectangle getBoundingBox() {
return boundingBox;
}
public Rectangle getBaseline() {
return baseline;
}
public List<Symbol> getSymbols() {
return symbols;
}
public Textline getParentTextline() {
return parentTextline;
}
public static class WordBuilder {
private final String word;
private final float confidence;
private final FontAttributes fontAttributes;
private final boolean fromDictionary;
private final boolean numeric;
private final Rectangle boundingBox;
private final Rectangle baseline;
private final List<Symbol> symbols = new ArrayList<Symbol>();
public WordBuilder(final String word, final float confidence, final FontAttributes fontAttributes, final boolean fromDictionary, final boolean numeric, final Rectangle boundingBox, final Rectangle baseline) {
this.word = word;
this.confidence = confidence;
this.fontAttributes = fontAttributes;
this.fromDictionary = fromDictionary;
this.numeric = numeric;
this.boundingBox = boundingBox;
this.baseline = baseline;
}
public WordBuilder addSymbol(final Symbol symbol) {
symbols.add(Objects.requireNonNull(symbol, "symbol"));
return this;
}
public Word build() {
return new Word(word, confidence, fontAttributes, fromDictionary, numeric, boundingBox, baseline, symbols);
}
}
private Object writeReplace() {
return new SerializationProxy(this);
}
private void readObject(final ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
private static class SerializationProxy implements Serializable {
private static final long serialVersionUID = 794943938877393932L;
private final String word;
private final float confidence;
private final FontAttributes fontAttributes;
private final boolean fromDictionary;
private final boolean numeric;
private final Rectangle boundingBox;
private final Rectangle baseline;
private final List<Symbol> symbols;
private final Textline parentTextline;
private SerializationProxy(final Word word) {
this.word = word.word;
this.confidence = word.confidence;
this.fontAttributes = word.fontAttributes;
this.fromDictionary = word.fromDictionary;
this.numeric = word.numeric;
this.boundingBox = word.boundingBox;
this.baseline = word.baseline;
this.symbols = word.symbols;
this.parentTextline = word.parentTextline;
}
private Object readResolve() {
Word localWord = new Word(word, confidence, fontAttributes, fromDictionary, numeric, boundingBox, baseline, symbols);
localWord.setParentTextline(parentTextline);
return localWord;
}
}
}
给出例外:
cannot assign instance of com.skiwi.tessutils4j.data.Word$SerializationProxy
to field com.skiwi.tessutils4j.data.Symbol$SerializationProxy.parentWord
of type com.skiwi.tessutils4j.data.Word in instance
of com.skiwi.tessutils4j.data.Symbol$SerializationProxy
请注意,这两个类并不是唯一具有此类依赖关系的类。
在这里评论我的设计:我有一个层次结构:
Block -> Paragraph -> Textline -> Word -> Symbol -> SymbolChoice
所有元素都需要有一个子列表,所有元素(SymbolChoice
除外)都需要知道它们的父元素。
如何避免此异常,可能会进行设计更改?
答案 0 :(得分:3)
这里的解决方案是要意识到父信息不值得在孩子中序列化。
在您的层次结构中:
Block -> Paragraph -> Textline -> Word -> Symbol -> SymbolChoice
如果你要序列化这些类中的任何一个,那么该类应该自行序列化,并且它是子类,但不是它的父类。可以在反序列化后设置父级。
换句话说,例如,您不应在符号parentWord
中拥有this.parentWord = symbol.parentWord;
和SerializationProxy
。相反,在Word的SerializationProxy
中,代码应如下所示:
private Object readResolve() {
Word localWord = new Word(word, confidence, fontAttributes, fromDictionary, numeric, boundingBox, baseline, symbols);
for (Symbol s : symbols) {
s.setParentWord(localWord);
}
return localWord;
}
这会更改引用的顺序以匹配反序列化顺序,并删除循环。