I believe this should be a really common case, yet I can't find any best practices. Assume I have the following class:
public class Equation {
private Operator operator;
private Object leftValue;
private Object rightValue;
// getters and setters
}
public enum Operator {...}
This class has been with us for some years already and is well used. Now I need to make it serializable. How do I do that?
Just add implements Serializable
In that case, the Equation
class only works as long as the values are Serializable
. Since equations only really work on numbers (maybe dates and strings?) that might work. But the values could be any kind of Object
, so there has to be a better way.
Make values Serializable
public class Equation implements Serializable{
private Operator operator;
private Serializable leftValue;
private Serializable rightValue;
// getters and setters
}
This works in any case, but these changes are an API break. And no matter what I do I need to change all the code using the class, which leads to potentially even more API breaks. For a big software system that might take ages.
Make values Serializable
, leave getters and setters as is
public void setLeftValue(Object leftValue) {
if (!(leftValue instanceof Serializable))
throw new IllegalArgumentException("Value must be Serializable!");
this.leftValue = leftValue;
}
This code breaks no existing API, but changes how the code behaves. Yet if I assume that all the values are Serializable
anyways, I feel like this might be the way to go. I can even put the new setters next to the old ones and deprecated them to make it obvious to future developers what objects to use.
Make values transient
:
At least that's what Sonar suggests. Yet it leads to an unusable class, at least in all the cases where we actually need Equation
to be Serializable
.
Create implementation that is Serializable
:
public class SerializableEquation extends Equation implements Serializable{
private Serializable leftValue;
private Serializable rightValue;
// override getters and setters
}
That way we would have to use an entire different class for serialization purposes, which seems kind of ugly, doesn't it?
Question:
What is a good way to handle this use case? I ideally don't want to break the API. And seeing as Java has yet to break the API there must be a way to handle cases like this.
If a super class implements Serializable, then its sub classes do automatically. When an instance of a serializable class is deserialized, the constructor doesn't run. If a super class doesn't implement Serializable, then when a subclass object is deserialized, the super class constructor will run.
Serialization is the process of converting an object into a stream of bytes to store the object or transmit it to memory, a database, or a file. Its main purpose is to save the state of an object in order to be able to recreate it when needed. The reverse process is called deserialization.
If you want a class object to be serializable, all you need to do it implement the java. io. Serializable interface. Serializable in java is a marker interface and has no fields or methods to implement.
Jackson actually doesn't need classes to implement Serializable but I'm going to add it anyway. Writing about a serialization framework and not implementing Serializable might seem strange.
In this kind of problem, a correct implementation should have used interfaces to limit the field of action.
What is the point the add an UrlConnection
to a String
? Well, this should have been something like this :
public class Equation {
Operator operator;
Operand leftOp, rightOp;
...
}
interface Operand {
...
}
And then, for specific type of data, you would have implements specific classs
public IntegerOperand implements Operand {
public Integer value;
...
}
From this, you only need to add the Serialiszable to Operator
and Operand
. This will be a contract that the dev need to follow, so every implementation need to be serializable (fully serializable since the interface asked it), this will be easy to test with JUnit.
In you case, you can't update the code because this would break the compatibility. So I would put the serialisation to the test, meaning that I woulld check if both Object instance ARE serializable, if not, then you do what you want with it.
You, either, check that with a method when you want (before serialisation) to prevent that this action can't be done with this data or with the setter of both value to restraint the possibilities.
public boolean isSerialisable(){
return Serializable.class.isAssignable(leftValue.class)
&& Serializable.class.isAssignable(rightValue.class);
}
This will be call before you need to serialize the instance, as a warning or error. (or directly in the setters if you want to break everything ;) )
Last resort, you serialize yourself the data, you can use some library to generate different type of structure, XML, JSON, .. or probably in bytes directly (no example in mind)
Here is a quick (and ugly example) of serialisation with 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;
}
}
}
By removing the implementation if serializable
from A
, this will failed. But like this, we see that this is working like expected.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With