"Неправильная десериализация лямбды", когда лямбда используется для реализации интерфейса

У меня есть проект калитки с лямбда-выражениями. На одной странице, когда пользователь нажимает кнопку "Назад", происходит сбой моего приложения:

java.lang.IllegalArgumentException: Invalid lambda deserialization
at x.y.z.MyPage$3.$deserializeLambda$(MyPage.java:1)

В классе страницы (куда я возвращаюсь) я использую лямбда-выражение для реализации этого интерфейса:

public interface Localizator extends Serializable {
    String getLocalizedString(String key);
}

И лямбда

protected void someMethod() {
    localize((String key) -> getString(key));
}

Когда я меняю лямбду на анонимный класс, все работает нормально. Как мне использовать лямбду в этом случае?

Конверт: Java 1.8.0_25, Netbeans 8.0.2, Wicket 6.17.0.

РЕДАКТИРОВАТЬ: Это реальный (но упрощенный) метод с лямбда:

@Override
protected DataLoader createDataLoader() {

    return new DataLoader(){

        @Override
        public List loadData() {
            ...
        }

        @Override
        public List convertToTableRows(List data) {
            return Converter.asRowList(
                data, 
                (Record record) -> {...}, // this lambda is OK
                (String key) -> getString(key)); // this lambda is crashing
        }

        @Override
        public List filterTableRow() {
            ...
        }

    };
}

Класс конвертера:

public class Converter implements Serializable { 
    public static ArrayList asRowList(List data, OtherLoader loader, Localizator localizator){...}

DataLoader также расширяет Serializable.

2 ответа

Включить Serialization поддержка, имеющая interface простирающийся Serializable достаточно. Тогда нет необходимости делать дополнительный бросок. На это указывает тот факт, что ваш код может сериализовать экземпляр лямбда-выражения. При десериализации произошел сбой, что является показателем несовместимости классов, поэтому, если вставка приведения помогла, весьма вероятно, что это был побочный эффект, поскольку при вставке приведения принудительная перекомпиляция класса, содержащего лямбда-выражение.


Если вы хотите продолжать использовать сериализованные лямбды, вы должны понимать, как это работает, как описано в документацииSerializedLambda:

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

SerializedLambda имеет readResolve метод, который ищет (возможно частный) статический метод, называемый $deserializeLambda$(SerializedLambda) в классе захвата вызывает его с первым аргументом и возвращает результат. Реализация лямбда-классов $deserializeLambda$ несут ответственность за проверку того, что свойства SerializedLambda соответствуют лямбде, фактически захваченной этим классом.

Итак, когда $deserializeLambda$ отклоняет десериализацию с исключением, это означает, что свойства сериализованной лямбды не соответствуют свойствам известной сериализуемой лямбды, определенной внутри класса. Эти свойства могут оказаться очень хрупкими.

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

В отличие от обычных классов, где вставка serialVersionUID может сказать платформе Serialization, что она не должна заботиться о явных несоответствиях, вы не можете сказать лямбда-выражения, чтобы показать большую надежность. Другими словами, после того, как вы сериализовали лямбда-экземпляры, которые должны сохраняться, вы не должны изменять их определяющий класс. Смотрите также этот ответ

Это не отвечает на вопрос выше, но эта ветка появляется первой при поиске Invalid lambda deserialization in Flink.

Если вы используете Proguard для обфускации перед публикацией :

  • Proguard портит Serializable классы

  • Вам нужно настроить proguard.proчтобы исправить эту ошибку, см. этот ответ .

В моем случае я реализовал настройки «Java 8 Lambda», предложенные в официальной документации некоторое время назад, но документация изменилась, и теперь мне не хватало более новых настроек, описанных там.

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