Я собираюсь представить несколько возможных способов поддержки старых версий класса / интерфейса в сериализации:
Использование Migrators
В таком случае вам нужно следующее в вашем Java-проекте:
- Неизменяемый интерфейс, объединяющий все эти классы
- Старые реализации этого интерфейса аннотированы как
@Deprecated
- Новая реализация вашего интерфейса
- Класс Migrator, помогающий преобразовать устаревший объект в новый
Позвольте мне с самого начала сказать, что не всегда возможно работать с объектами более старых версий (см. Документацию Oracle по версиям сериализуемых объектов ). Для простоты давайте предположим, что при обновлении ваши классы всегда реализуют интерфейс IEntity
, который вы определили.
public interface IEntity extends Serializable {
// your method definitions
}
И предположим, что вы изначально работаете с классом:
public class Entity implements IEntity {
private static final long serialVersionUID = 123456789L;
// fields and methods
}
Если вам нужно обновить класс Entity
до новой реализации с новым serialVersionUID, то сначала аннотируйте его как @Deprecated
, но не переименовывайте его и не перемещайте это в другой пакет :
@Deprecated
public class Entity implements IEntity {
private static final long serialVersionUID = 123456789L;
// fields and methods
}
Теперь создайте новую реализацию с новым serialVersionUID (важно) и дополнительным конструктором следующим образом ...
public class Entity_new implements IEntity {
private static final long serialVersionUID = 5555558L;
public Entity_new(IEntity){
// Create a new instance of Entity_new copying the given IEntity
}
}
Конечно, наиболее важной частью всей процедуры является то, как вы реализуете конструктор выше. Если вы сериализовали некоторые объекты старого типа Entity
в виде двоичных файлов (используя, например, ObjectOutputStream
), и теперь вы перешли на Entity_new
, вы можете проанализировать их как экземпляры Entity
и затем преобразовать их в экземпляры Entity_new
. Вот пример:
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);
}
}
Есть, конечно, другая альтернатива, которая не требует конкретного конструктора или не использует java отражение . Есть много других дизайнерских подходов, которые можно выбрать. Обратите также внимание на то, что обработка исключений и проверки для нулевых объектов были полностью опущены для простоты в приведенном выше коде.
Разработка универсального сериализуемого интерфейса
Если применимо, вы можете в первую очередь попытаться разработать интерфейс для вашего класса, который вряд ли изменится в будущем. Если вашему классу необходимо хранить набор свойств, которые с большой вероятностью могут быть изменены, рассмотрите возможность использования Map<String, Object>
для этой цели, где String
относится к имени / идентификатору свойства, а Object
- соответствующее значение.
Настройка readObject и writeObject
Существует еще один способ обеспечения поддержки более старой версии, который я упомяну для полноты, но я бы не выбрал его. Вы можете реализовать private void readObject(ObjectInputStream in)
и private void writeObject(ObjectOutputStream out)
таким образом, чтобы он соответствовал как текущей, так и всем предыдущим версиям вашего класса / интерфейса. Я не уверен, что что-то подобное всегда возможно и устойчиво, и у вас может получиться очень грязная и длительная реализация этих методов.
Альтернативные методы сериализации
Это не отвечает на вопрос ОП, но я считаю, что стоит поднять его. Вы можете рассмотреть сериализацию вашего объекта в некотором формате ASCII, таком как JSON, YAML или XML. В таком случае, если вы сильно не перепроектируете свой сериализуемый интерфейс, расширяемость выходит из коробки. BSON (двоичный JSON) - хороший выбор, если вы ищете расширяемый двоичный протокол. Возможно, это лучший способ обеспечить переносимость ваших объектов между программами, которые могут быть не реализованы в Java.