Сериализованная лямбда и нет serialVersionUID?

Я пытаюсь узнать, как сериализация работает с Java и его последней версией. Я пытаюсь сериализовать лямбду следующим образом:

Runnable r = (Runnable & Serializable)() -> {System.out.println("This is a test");};

Но я замечаю, что у меня нет предупреждения об отсутствии serialVersionUID переменная. Это нормально?

Я знаю, что он будет сгенерирован во время выполнения, однако настоятельно рекомендуется определить его: https://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html

Если сериализуемый класс явно не объявляет serialVersionUID, тогда среда выполнения сериализации вычислит значение serialVersionUID по умолчанию для этого класса на основе различных аспектов класса, как описано в Спецификации сериализации объектов Java(TM). Однако настоятельно рекомендуется, чтобы все сериализуемые классы явно объявляли значения serialVersionUID, так как вычисление serialVersionUID по умолчанию очень чувствительно к деталям класса, которые могут различаться в зависимости от реализаций компилятора, и, следовательно, могут привести к неожиданным исключениям InvalidClassExceptions во время десериализации. Поэтому, чтобы гарантировать согласованное значение serialVersionUID в различных реализациях Java-компилятора, сериализуемый класс должен объявить явное значение serialVersionUID. Также настоятельно рекомендуется, чтобы в явных объявлениях serialVersionUID использовался частный модификатор, где это возможно, поскольку такие объявления применяются только к немедленно объявленному классу - поля serialVersionUID не используются в качестве унаследованных членов. Классы массива не могут объявлять явный serialVersionUID, поэтому у них всегда есть вычисленное значение по умолчанию, но требование соответствия значений serialVersionUID отменяется для классов массива.

Что я должен делать? Как я могу определить это в моей лямбде?

Спасибо

1 ответ

Решение

serialVersionUID относится только к классам, которые генерируют идентификатор потока. Это не тот случай, если сериализуемый класс имеет writeReplace() метод (также описан в Serializable документация), которая возвращает замещающий объект другого класса, поскольку такое представление полностью отделено от исходного класса. Это то, что происходит с сериализуемыми экземплярами лямбда, см. SerializedLambda:

Ожидается, что разработчики сериализуемых лямбд, таких как компиляторы или языковые библиотеки времени выполнения, обеспечат правильную десериализацию экземпляров. Одним из способов сделать это является обеспечение того, чтобы writeReplace метод возвращает экземпляр SerializedLambda вместо того, чтобы продолжать сериализацию по умолчанию.

Так что это пример SerializedLambda это заканчивается в потоке и, следовательно, ответственность этого класса иметь стабильное сериализованное представление. К сожалению, это не защитит вас от возможных несовместимостей.

После десериализации будет вызван синтетический метод класса, определяющего лямбда-выражение (сравните с этим и этим ответом), который отклонит попытки десериализации, которые не соответствуют существующему определению лямбда-выражения в этом классе, тогда как сопоставление может зависеть от тонкие аспекты лямбда-определения. Обратите внимание, что даже перекомпиляция определяющего класса с Eclipse, а не javac может нарушить совместимость сериализации.

Не также влияние на безопасность Serializable лямбды Как правило, я рекомендую избегать его использования.

Другие вопросы по тегам