I will introduce several possible ways to support old versions of a class / interface in serialization:
Use Migars
In this case, you will need the following in your Java project:
- The immutable interface that unites all these classes
- Old implementation (s) of this interface annotated as
@Deprecated - New implementation of your interface
- The Migrator class that helps you convert an obsolete object to a new one.
Let me say from the very beginning that it is not always possible to work with objects of older versions (see Oracle documentation on version control of serializable objects ). For simplicity, suppose that when upgrading, your classes always implement the IEntity interface that you defined.
public interface IEntity extends Serializable {
And suppose you are initially working with a class:
public class Entity implements IEntity { private static final long serialVersionUID = 123456789L;
If you need to update the Entity class in a new implementation with the new serialVersionUID, first annotate it as @Deprecated , but do not rename it and do not move it to another package:
@Deprecated public class Entity implements IEntity { private static final long serialVersionUID = 123456789L;
Now create a new implementation with a new serialVersionUID (important) and an additional constructor, as follows ...
public class Entity_new implements IEntity { private static final long serialVersionUID = 5555558L; public Entity_new(IEntity){
Of course, the most important part of the whole procedure is how you implement the above constructor. If you serialized some objects of the old Entity type as binary files (using, for example, ObjectOutputStream ), and now you switched to Entity_new , you can analyze them as Entity instances and then convert them to Entity_new instances. Here is an example:
public class Migrator { private final IEntity entity; private Class<? extends IEntity> newestClass = Entity_new.class; public Migrator(final IEntity entity){ this.entity = entity; } public Migrator setNewestClass(Class<? extends IEntity> clazz){ this.newestClass = clazz; return this; } public IEntity migrate() throws Exception { Constructor<? extends IEntity> constr = newestClass.getConstructor(IEntity.class); return constr.newInstance(this.entity); } }
There is, of course, another alternative that does not require this particular constructor or uses java reflection . There are many other design approaches you can choose. Also note that exception handling and checking for null objects have been completely omitted for simplicity in the code above.
Development of a universal serializable interface
If applicable, you can try first of all to develop an interface for your class, which is unlikely to change in the future. If your class needs to save a set of properties that are likely to change, consider using Map<String, Object> for this purpose, where String refers to the name / identifier of the property and Object refers to the corresponding value.
Configure readObject and writeObject
There is another way to provide support for the older version, which I will talk about for completeness, but not the one I would choose. You can implement private void readObject(ObjectInputStream in) and private void writeObject(ObjectOutputStream out) so that it contains both the current and all previous versions of your class / interface. I'm not sure that something like this is always doable and sustainable, and you can end up having a very dirty and lengthy implementation of these methods.
Alternative serialization methods
This does not answer the OP question, but I think it is worth raising it. You might consider serializing your object in some ASCII format, such as JSON, YAML, or XML. In this case, if you are not decisively redesigning your serializable interface, extensibility fails. BSON (binary JSON) is a good choice if you are looking for an extensible binary protocol. Perhaps this is the best way to ensure the portability of your objects through software that cannot be implemented in Java.