¿Cómo serializar una lambda?


157

¿Cómo puedo serializar elegantemente una lambda?

Por ejemplo, el siguiente código arroja a NotSerializableException. ¿Cómo puedo solucionarlo sin crear una SerializableRunnableinterfaz "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();
    }
}

18
Si bien esto es posible (ver la respuesta seleccionada), probablemente todos deberían pensarlo dos veces antes de hacerlo. Está oficialmente "fuertemente desaconsejado" y puede tener serias implicaciones de seguridad .
David

Respuestas:


260

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

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

Y la lambda se vuelve serializable automáticamente.


44
Muy interesante: esta característica parece ser bastante poderosa. ¿Hay algún uso de tal expresión de fundición fuera de fundir lambdas? Por ejemplo, ¿ahora también es posible hacer algo similar con una clase anónima ordinaria?
Balder

66
@Balder Se agregó la facilidad para lanzar a un tipo de intersección para proporcionar un tipo de destino para la inferencia de tipos de lambdas. Dado que los AIC tienen un tipo de manifiesto (es decir, su tipo no se infiere), no es útil emitir un AIC a un tipo de intersección. (Es posible, pero no útil). Para que un AIC implemente varias interfaces, debe crear una nueva subinterfaz que las extienda a todas y luego crear una instancia.
Stuart Marks el

2
¿Producirá esto una advertencia del compilador, diciendo que no se define serialVersionUID?
Kirill Rakhman

11
Nota: esto solo funciona si aplica el yeso durante la construcción. Lo siguiente arrojará una ClassCastException: Runnable r = () -> System.out.println ("Serializable!"); Runnable serializableR = (Runnable & Serializable) r;
bcody


24

La misma construcción se puede utilizar 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.


18

Reparto muy feo. Prefiero definir una extensión serializable para 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 el lambda puede definirse como tal:

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

y llamando a la función puedes pasar tu lambda sin ningún reparto feo:

someFunction(arg -> doXYZ(arg));

2
Me gusta esta respuesta porque entonces cualquier llamador externo que no escriba también será serializable automáticamente. Si desea que los objetos enviados sean serializables, su interfaz debe ser serializable, que es una especie de punto de una interfaz. Sin embargo, la pregunta decía "sin crear una SerializableRunnableinterfaz 'ficticia'"
slevin

4

En caso de que alguien caiga aquí mientras crea el código Beam / Dataflow:

Beam tiene su propia interfaz de función serializable, por lo que no necesita una interfaz ficticia o modelos detallados.


3

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

  1. Modifique el InnerClassLambdaMetafactorypara generar siempre el código requerido para la serialización
  2. Llame directamente al LambdaMetaFactorydurante la deserialización

Para detalles y código, vea esta publicación de blog

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.