【问题标题】:SerializationProxy pattern gives ClassCastException: How to avoid?SerializationProxy 模式给出 ClassCastException:如何避免?
【发布时间】:2014-06-04 13:25:52
【问题描述】:

我有以下两个类,它们使用 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 除外)都需要知道它们的父元素。

如何避免这种异常,可能需要进行设计更改?

【问题讨论】:

    标签: java serialization classcastexception circular-reference


    【解决方案1】:

    这里的解决方案是意识到父信息不值得在子中序列化。

    在您的层次结构中:

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

    如果您要序列化这些类中的任何一个,则该类应该序列化自己,并且它是子类,而不是父类。反序列化后可以设置父级。

    换句话说,例如,符号的SerializationProxy 中不应有parentWordthis.parentWord = symbol.parentWord;。相反,在 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;
        }
    

    这会更改引用顺序以匹配反序列化顺序,并删除循环。

    【讨论】:

    • 如果你有一个无环有向图,那就太好了。但是,如果对象引用可传递地引用封闭实体,那么您就是 SOL。如果关系不是父子关系,这很容易实现。想象一下序列化多对多关系的一侧,其中关系由连接实体表示。这是 JPA 中非常常见的情况——规范要求所有实体都实现可序列化。如果您还尝试在这种情况下使用 SerializationProxy,您将遇到 Java 序列化的严重缺陷。
    猜你喜欢
    • 2016-03-31
    • 1970-01-01
    • 2017-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多