assylias
assylias

Reputation: 328785

How to serialize a lambda?

How can I elegantly serialize a lambda?

For example, the code below throws a NotSerializableException. How can I fix it without creating a SerializableRunnable "dummy" interface?

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

Upvotes: 176

Views: 59217

Answers (6)

cquezel
cquezel

Reputation: 4497

To add to other answers, you can create a serializable lambda by using the cast expression or by using a new interface that extends Serializable as shown in other answers. Notice that the resulting objects don't necessarily play nice with default methods of the functional interfaces (for example). The third solution, far from ideal because it does not ensure that the right hand side operand is serializable, does generate a serializable object.

@Test
public void testCasetExpression() {
    Predicate<Integer> p1 = (Predicate<Integer> & Serializable)i -> true;
    Predicate<Integer> p2 = (Predicate<Integer> & Serializable)i -> true;
    
    Predicate<Integer> p3 = p1.and(p2);
    Assertions.assertFalse(p3 instanceof Serializable); // Notice false
}

interface SerializablePredicateV1<T> extends Predicate<T>, Serializable {}

@Test
public void testInterfaceV1() {
    Predicate<Integer> p1 = (SerializablePredicateV1)i -> true;
    Predicate<Integer> p2 = (SerializablePredicateV1)i -> true;
    
    Predicate<Integer> p3 = p1.and(p2);
    Assertions.assertFalse(p3 instanceof Serializable); // Notice false
}

interface SerializablePredicateV2<T> extends Predicate<T>, Serializable {
    
    @Override
    default SerializablePredicateV2<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

}

@Test
public void testInterfaceV2() {
    Predicate<Integer> p1 = (SerializablePredicateV2)i -> true;
    Predicate<Integer> p2 = (SerializablePredicateV2)i -> true;
    
    Predicate<Integer> p3 = p1.and(p2);
    Assertions.assertTrue(p3 instanceof Serializable); // Notice true
    
    Predicate<Integer> p4 = i -> true; // Not serializable
    
    Predicate<Integer> p5 = p1.and(p4);
    // true but will fail because p4 is not serializable
    Assertions.assertTrue(p5 instanceof Serializable);  
}

// Not a Predicate anymore :(
interface SerializablePredicateV3<T> extends Serializable {

    boolean test(T t);

    default SerializablePredicateV3<T> and(SerializablePredicateV3<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }
}

@Test
public void testInterfaceV3() {
    SerializablePredicateV3<Integer> p1 = (SerializablePredicateV3)i -> true;
    SerializablePredicateV3<Integer> p2 = (SerializablePredicateV3)i -> true;
    
    SerializablePredicateV3<Integer> p3 = p1.and(p2);
}

Upvotes: 0

ruediste
ruediste

Reputation: 2959

If you are willing to switch to another serialization framework like Kryo, you can get rid of the multiple bounds or the requirement that the implemented interface must implement Serializable. The approach is to

  1. Modify the InnerClassLambdaMetafactory to always generate the code required for serialization
  2. Directly call the LambdaMetaFactory during deserialization

For details and code see this blog post

Upvotes: 4

Rafa&#235;l
Rafa&#235;l

Reputation: 1027

In case someone falls here while creating Beam/Dataflow code :

Beam has his own SerializableFunction Interface so no need for dummy interface or verbose casts.

Upvotes: 9

Pascal
Pascal

Reputation: 415

Very ugly cast. I prefer to define a Serializable extension to the functional interface I'm using

For example:

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

then the method accepting the lambda can be defined as such :

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

and calling the function you can pass your lambda without any ugly cast:

someFunction(arg -> doXYZ(arg));

Upvotes: 30

Vicente Romero
Vicente Romero

Reputation: 1460

The same construction can be used for method references. For example this 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);
    }
}

defines a lambda expression and a method reference with a serializable target type.

Upvotes: 25

assylias
assylias

Reputation: 328785

Java 8 introduces the possibility to cast an object to an intersection of types by adding multiple bounds. In the case of serialization, it is therefore possible to write:

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

And the lambda automagically becomes serializable.

Upvotes: 303

Related Questions