Як серіалізувати лямбда?


157

Як можна елегантно серіалізувати лямбда?

Наприклад, наведений нижче код містить "a" NotSerializableException. Як я можу це виправити, не створюючи SerializableRunnable"фіктивного" інтерфейсу?

public static void main(String[] args) throws Exception {
    File file = Files.createTempFile("lambda", "ser").toFile();
    try (ObjectOutput oo = new ObjectOutputStream(new FileOutputStream(file))) {
        Runnable r = () -> System.out.println("Can I be serialized?");
        oo.writeObject(r);
    }

    try (ObjectInput oi = new ObjectInputStream(new FileInputStream(file))) {
        Runnable  r = (Runnable) oi.readObject();
        r.run();
    }
}

18
Хоча це можливо (див. Обрану відповідь), кожен, мабуть, повинен двічі подумати над тим, що насправді робити це. Це офіційно "сильно не рекомендується" і може мати серйозні наслідки для безпеки .
Девід

Відповіді:


260

Java 8 вводить можливість віддати об'єкт до перетину типів, додавши декілька меж . У випадку серіалізації, отже, можна написати:

Runnable r = (Runnable & Serializable)() -> System.out.println("Serializable!");

А лямбда автоматично стає серійним.


4
Дуже цікаво - ця функція здається досить потужною. Чи є вживання такого виразного складу поза кастинг лямбдів? Наприклад, чи можливо зараз зробити щось подібне зі звичайним анонімним класом?
Балдер

6
@Balder Споруда для приведення до типу перехрестя була додана, щоб забезпечити цільовий тип для виведення типу лямбда. Оскільки АПК мають маніфестний тип (тобто його тип не робиться), закидання АПК у тип перетину не є корисним. (Можливо, просто не корисно.) Щоб AIC реалізував декілька інтерфейсів, ви повинні створити новий підінтерфейс, який розширює їх усі, а потім інстанціювати це.
Стюарт Маркс

2
Чи створить це попередження компілятора, кажучи, що не визначено serialVersionUID?
Кирило Рахман

11
Примітка: це працює лише в тому випадку, якщо ви застосовуєте литий під час будівництва. Наступним буде кинути ClassCastException: Runnable r = () -> System.out.println ("Serializable!"); Runnable serializableR = (Runnable & Serializable) r;
bcody

3
@bcody Так дивись також: stackoverflow.com/questions/25391656 / ...
assylias

24

Ця ж конструкція може бути використана для посилань на методи. Наприклад цей код:

import java.io.Serializable;

public class Test {
    static Object bar(String s) {
        return "make serializable";
    }

    void m () {
        SAM s1 = (SAM & Serializable) Test::bar;
        SAM s2 = (SAM & Serializable) t -> "make serializable";
    }

    interface SAM {
        Object action(String s);
    }
}

визначає лямбда-вираз та посилання на метод із серіалізаційним цільовим типом.


18

Дуже некрасивий акторський склад. Я вважаю за краще визначити розширення Serializable до функціонального інтерфейсу, який я використовую

Наприклад:

interface SerializableFunction<T,R> extends Function<T,R>, Serializable {}
interface SerializableConsumer<T> extends Consumer<T>, Serializable {}

тоді метод прийняття лямбда може бути визначений як такий:

private void someFunction(SerializableFunction<String, Object> function) {
   ...
}

і зателефонувавши за функцією, ви можете передавати лямбда без потворних ролей:

someFunction(arg -> doXYZ(arg));

2
Мені подобається ця відповідь, тому що тоді будь-який зовнішній абонент, який ви не пишете, автоматично також буде серійним. Якщо ви хочете, щоб представлені об'єкти були серіалізаційними, ваш інтерфейс повинен бути серіалізаційним, що є своєрідною точкою інтерфейсу. Однак питання все-таки говорило "без створення SerializableRunnable" фіктивного "інтерфейсу
slevin

4

Якщо хтось потрапляє сюди під час створення коду Beam / Dataflow:

У Beam є власний інтерфейс SerializableFunction, тому немає необхідності в фіктивних інтерфейсах або багатослівних кастрах.


3

Якщо ви готові перейти на іншу рамку серіалізації, на зразок Kryo , ви можете позбутися від декількох меж або вимоги, яку повинен реалізувати реалізований інтерфейс Serializable. Підхід до

  1. Змініть InnerClassLambdaMetafactory завжди генерувати код, необхідний для серіалізації
  2. Безпосередньо зателефонуйте LambdaMetaFactoryпід час десеріалізації

Детальніше та код див. У цій публікації в блозі

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.