2

У меня есть следующие два класса, которые используют Сериализационный прокси шаблон от Эффективная Java книга. Я полагаю, что я столкнулся с проблемами из-за круговых зависимостей, как бы я мог ее решить?SerializationProxy шаблон дает ClassCastException: как этого избежать?

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) должны знать своих родителей.

Как я могу избежать этого исключения, возможно, с изменением дизайна?

ответ

3

Решение здесь заключается в понимании того, что родительская информация не стоит сериализовать в дочернем.

В иерархии:

Block -> Paragraph -> Textline -> Word -> Symbol -> SymbolChoice 

если вы сериализация любой из этих классов, что класс должен сериализовать себя, и это дети, но не его родители. Родитель может быть установлен после десериализации.

Другими словами, например, у вас не должно быть parentWord и this.parentWord = symbol.parentWord; в Символическом SerializationProxy. Вместо этого, в что Слово 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; 
    } 

Это меняет порядок ссылок, чтобы соответствовать порядку десериализации, и удаляет циклы.