¿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();
    }
}
asilias
fuente
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.

asilias
fuente
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
3
@bcody Sí, vea también: stackoverflow.com/questions/25391656/…
assylias
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.

Vicente Romero
fuente
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));
Pascal
fuente
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.

Rafaël
fuente
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

ruediste
fuente