Cómo serializar una lambda?


¿Cómo puedo serializar elegantemente una lambda?

Por ejemplo, el siguiente código arroja un NotSerializableException. ¿Cómo puedo arreglarlo sin crear una interfaz SerializableRunnable "ficticia"?

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();
    }
}
Author: assylias, 2014-04-02

4 answers

Java 8 introduce la posibilidad de enviar un objeto a una intersección de tipos agregando múltiples límites. En el caso de serialización, por lo tanto, es posible escribir:

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

Y la lambda automáticamente se vuelve serializable.

 213
Author: assylias,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-02 10:09:49

Se puede usar la misma construcción para referencias de métodos. Por ejemplo este código:

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);
    }
}

Define una expresión lambda y una referencia de método con un tipo de destino serializable.

 19
Author: Vicente Romero,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-25 01:01:01

Reparto muy feo. Prefiero definir una extensión serializable a la interfaz funcional que estoy usando

Por ejemplo:

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

Entonces el método que acepta la lambda se puede definir como tal:

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

Y llamando a la función puede pasar su lambda sin ningún feo molde:

someFunction(arg -> doXYZ(arg));
 8
Author: Pascal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-15 18:27:54

Si está dispuesto a cambiar a otro framework de serialización como Kryo, puede deshacerse de los múltiples límites o del requisito de que la interfaz implementada debe implementar Serializable. El enfoque es

  1. Modifica el InnerClassLambdaMetafactory para generar siempre el código necesario para la serialización
  2. Llame directamente al LambdaMetaFactory durante la deserialización

Para los detalles y el código ver este entrada de blog

 2
Author: ruediste,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-07 11:59:34