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();
}
}
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.
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.
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));
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 à
InnerClassLambdaMetafactory
pour toujours générer le code requis pour la sérialisationLambdaMetaFactory
pendant la désérialisationPour plus de détails et le code, voir ceci blog post
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.