我认为这应该是一个非常常见的案例,但我无法找到任何最佳做法。假设我有以下课程:
public class Equation {
private Operator operator;
private Object leftValue;
private Object rightValue;
// getters and setters
}
public enum Operator {...}
这门课已经和我们一起使用了好几年了,并且使用得很好。现在我需要使它可序列化。我该怎么做?
只需添加implements Serializable
在这种情况下,Equation
类仅在值为Serializable
时才有效。方程式只对数字(可能是日期和字符串?)真正起作用。但是值可以是任何类型Object
,因此必须有更好的方法。
制作值Serializable
public class Equation implements Serializable{
private Operator operator;
private Serializable leftValue;
private Serializable rightValue;
// getters and setters
}
这在任何情况下都有效,但这些更改是API中断。无论我做什么,我都需要使用类更改所有代码,这可能导致更多的API中断。对于可能需要很长时间的大型软件系统。
创建值Serializable
,保持getter和setter不变
public void setLeftValue(Object leftValue) {
if (!(leftValue instanceof Serializable))
throw new IllegalArgumentException("Value must be Serializable!");
this.leftValue = leftValue;
}
此代码不会破坏现有的API,但会更改代码的行为方式。然而,如果我假设所有的值都是Serializable
,我觉得这可能是要走的路。我甚至可以将新的setter放在旧的setter旁边,并弃用它们以使未来的开发人员明白要使用的对象。
制作值transient
:
至少是Sonar建议的那个。然而,它导致一个无法使用的课程,至少在我们实际需要Equation
为Serializable
的所有情况下。
创建Serializable
的实施:
public class SerializableEquation extends Equation implements Serializable{
private Serializable leftValue;
private Serializable rightValue;
// override getters and setters
}
这样我们就不得不使用整个不同的类来进行序列化,这看起来有点难看,不是吗?
问题:
处理此用例的好方法是什么?我理想情况下不想打破API。看到Java尚未打破API,必须有办法处理这样的情况。
答案 0 :(得分:1)
在这种问题中,正确的实现应该使用接口来限制行动领域。
将UrlConnection
添加到String
有什么意义?嗯,这应该是这样的:
public class Equation {
Operator operator;
Operand leftOp, rightOp;
...
}
interface Operand {
...
}
然后,对于特定类型的数据,您将实现特定的类
public IntegerOperand implements Operand {
public Integer value;
...
}
由此,您只需将Serialiszable添加到Operator
和Operand
。这将是开发人员需要遵循的合同,因此每个实现都需要可序列化(完全可序列化,因为接口要求它),这将很容易使用JUnit进行测试。
在您的情况下,您无法更新代码,因为这会破坏兼容性。所以我会把序列化放到测试中,这意味着我会检查对象实例是否可序列化,如果没有,那么你就可以用它做你想做的事。
您也可以根据需要(在序列化之前)使用方法检查,以防止无法使用此数据或使用这两个值的setter来完成此操作,从而限制可能性。
public boolean isSerialisable(){
return Serializable.class.isAssignable(leftValue.class)
&& Serializable.class.isAssignable(rightValue.class);
}
这将在您需要序列化实例之前调用,作为警告或错误。 (或者如果你想打破一切,直接在制定者中;)
最后,你自己序列化数据,你可以使用一些库生成不同类型的结构,XML,JSON,..或者可能直接以字节为单位(没有例子)
以下是使用Object
进行序列化的快速(丑陋示例)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;
}
}
}
如果从serializable
A
移除实施,则会失败。但是像这样,我们发现这是预期的。