将Serializable引入现有代码

Ste*_* S. 6 java

我认为这应该是一个非常普遍的案例,但我找不到任何最佳做法.假设我有以下课程:

public class Equation {

    private Operator operator;
    private Object leftValue;
    private Object rightValue;

    // getters and setters
}

public enum Operator  {...}
Run Code Online (Sandbox Code Playgroud)

这堂课已经和我们一起使用了好几年了,而且使用得很好.现在我需要使它可序列化.我怎么做?

只需添加 implements Serializable

在这种情况下,Equation只有值有效,该类才有效Serializable.方程式只对数字(可能是日期和字符串?)真正起作用.但价值观可能是任何一种Object,所以必须有更好的方法.

创造价值 Serializable

public class Equation implements Serializable{

    private Operator operator;
    private Serializable leftValue;
    private Serializable rightValue;

    // getters and setters 
}
Run Code Online (Sandbox Code Playgroud)

这在任何情况下都有效,但这些更改是API中断.无论我做什么,我都需要使用类更改所有代码,这可能导致更多的API中断.对于可能需要很长时间的大型软件系统.

制作价值观Serializable,让getter和setter保持原样

public void setLeftValue(Object leftValue) {
    if (!(leftValue instanceof Serializable)) 
        throw new IllegalArgumentException("Value must be Serializable!");
    this.leftValue = leftValue;
}
Run Code Online (Sandbox Code Playgroud)

此代码不会破坏现有API,但会更改代码的行为方式.然而,如果我认为所有的价值观都是如此Serializable,我觉得这可能是要走的路.我甚至可以将新的setter放在旧的setter旁边,并弃用它们以使未来的开发人员明白要使用的对象.

创造价值transient:

至少这是Sonar建议的.然而,它会导致无法使用的类,至少在我们实际需要的所有案件EquationSerializable.

创建以下实现Serializable:

public class SerializableEquation extends Equation implements Serializable{

    private Serializable leftValue;
    private Serializable rightValue;

    // override getters and setters
}
Run Code Online (Sandbox Code Playgroud)

这样我们就不得不使用整个不同的类来进行序列化,这看起来有点难看,不是吗?

题:

处理这个用例的好方法是什么?我理想情况下不想打破API.看到Java尚未打破API,必须有办法处理这样的情况.

Axe*_*elH 1

在这种问题中,正确的实现应该使用接口来限制作用范围。

UrlConnection将 an 添加到 a有什么意义String?好吧,这应该是这样的:

public class Equation {
    Operator operator;
    Operand leftOp, rightOp;

    ...
}

interface Operand {

    ...
}
Run Code Online (Sandbox Code Playgroud)

然后,对于特定类型的数据,您将实现特定的类

public IntegerOperand implements Operand {

    public Integer value;
    ...
}
Run Code Online (Sandbox Code Playgroud)

由此,您只需将 Serialiszable 添加到OperatorOperand。这将是开发人员需要遵循的契约,因此每个实现都需要可序列化(由于接口要求它而完全可序列化),这将很容易使用 JUnit 进行测试。

在您的情况下,您无法更新代码,因为这会破坏兼容性。所以我会测试序列化,这意味着我将检查两个对象实例是否可序列化,如果不是,那么你可以用它做你想做的事情。

您也可以在需要时(在序列化之前)使用方法进行检查,以防止无法使用此数据或使用两个值的设置器来完成此操作以限制可能性。

public boolean isSerialisable(){ 
    return Serializable.class.isAssignable(leftValue.class)
            && Serializable.class.isAssignable(rightValue.class);
}
Run Code Online (Sandbox Code Playgroud)

这将在您需要序列化实例之前调用,作为警告或错误。(或者如果你想破坏一切,则直接在设置器中;))

最后的手段,你自己序列化数据,你可以使用一些库来生成不同类型的结构,XML,JSON,..或者可能直接以字节为单位(没有例子)

编辑:

这是一个使用对象进行序列化的快速(且丑陋的示例)

public class Main implements Serializable{

    public Object value;

    public Main(Object o){ this.value = o; }

    public static void main(String[] args){
        Main m = new Main(new A());

        try {
             FileOutputStream fileOut = new FileOutputStream("test.ser");
             ObjectOutputStream out = new ObjectOutputStream(fileOut);
             out.writeObject(m);
             out.close();
             fileOut.close();
        }catch(IOException i) {
            i.printStackTrace();
        }

        m = null;
        try {
            FileInputStream fileIn = new FileInputStream("test.ser");
            ObjectInputStream in = new ObjectInputStream(fileIn);
            m = (Main) in.readObject();
            in.close();
            fileIn.close();
        }catch(Exception i) {
            i.printStackTrace();
            return;
        }
        System.out.println(" M : " + m);
    }

    @Override
    public String toString() {
        return value == null ? "null" : value.toString();
    }

    static class A implements Serializable{
        String s = "foo";

        @Override
        public String toString() {
            return s;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

serializable通过删除 if from 的实现A,这将失败。但像这样,我们看到它正在按预期工作。