web-dev-qa-db-fra.com

Comment sérialiser un lambda?

Comment puis-je élégamment sérialiser un lambda?

Par exemple, le code ci-dessous jette un NotSerializableException. Comment puis-je résoudre ce problème sans créer une interface "factice" 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();
    }
}
147
assylias

Java 8 introduit la possibilité de attribuer un objet à une intersection de types en ajoutant plusieurs bornes . En cas de sérialisation, il est donc possible d'écrire:

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

Et le lambda devient automatiquement sérialisable.

246
assylias

La même construction peut être utilisée pour les références de méthode. Par exemple ce code:

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

définit une expression lambda et une référence de méthode avec un type cible sérialisable.

22
Vicente Romero

Très moche casting. Je préfère définir une extension Serializable sur l'interface fonctionnelle que j'utilise

Par exemple:

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

alors la méthode acceptant le lambda peut être définie comme telle:

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

et en appelant la fonction, vous pouvez faire passer votre lambda sans la moindre apparence laide:

someFunction(arg -> doXYZ(arg));
14
Pascal

Si vous souhaitez passer à un autre framework de sérialisation tel que Kryo , vous pouvez supprimer les limites multiples ou l'exigence selon laquelle l'interface implémentée doit implémenter Serializable. L’approche consiste à

  1. Modifiez le InnerClassLambdaMetafactory pour toujours générer le code requis pour la sérialisation
  2. Appeler directement le LambdaMetaFactory pendant la désérialisation

Pour plus de détails et le code, voir ceci blog post

3
ruediste

Si quelqu'un tombe ici en créant un code Beam/Dataflow:

Beam a sa propre interface SerializableFunction , vous n'avez donc pas besoin d'interface factice ni de distribution verbeuse.

1
Rafaël