174

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();
    }
}
assylias
  • 321,522
  • 82
  • 660
  • 783
  • 18
    While this is possible (see the selected answer), everyone should probably think twice about actually doing this. It is officially ["strongly discouraged"](https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#serialization) and can have [serious](http://stackoverflow.com/questions/25443655/possibility-to-explicit-remove-serialization-support-for-a-lambda) [security](https://www.contrastsecurity.com/security-influencers/serialization-must-die-act-1-kryo) [implications](https://www.contrastsecurity.com/security-influencers/serialization-must-die-act-2-xstream). – David Mar 10 '16 at 09:32

5 Answers5

296

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.

assylias
  • 321,522
  • 82
  • 660
  • 783
  • 4
    Very interesting - this feature seems to be quite powerful. Is there any use of such a cast expression outside of casting lambdas? E.g. is it now also possible to do something similar with an ordinary anonymous class? – Balder Apr 02 '14 at 10:50
  • What I meant was: is there any way to instantiate an anonymous class implementing two interfaces similar to the instantiated lambda expression in your example. But I just looked into it and it seems there is no new syntax for doing something like that. – Balder Apr 02 '14 at 11:05
  • 6
    @Balder The facility to cast to an intersection type was added in order to provide a target type for type inference of lambdas. Since AICs have a manifest type (i.e., its type is not inferred) casting an AIC to an intersection type isn't useful. (It is possible, just not useful.) To have an AIC implement multiple interfaces, you have to create a new subinterface that extends all of them, and then instantiate that. – Stuart Marks Apr 02 '14 at 17:19
  • 2
    Will this produce a compiler warning, saying no serialVersionUID is defined? – Kirill Rakhman Apr 06 '14 at 13:09
  • 2
    @cypressious No compiler warning. – assylias Apr 07 '14 at 09:31
  • can SerializedLambda another alternative for serialization ? – daimon Sep 15 '15 at 07:48
  • 16
    Note: this only works if you apply the cast during construction. The following will throw a ClassCastException: Runnable r = () -> System.out.println("Serializable!"); Runnable serializableR = (Runnable & Serializable)r; – bcody Oct 09 '15 at 07:36
  • 3
    @bcody Yes see also: http://stackoverflow.com/questions/25391656/serialization-of-a-lambda-after-its-creation – assylias Oct 09 '15 at 08:55
  • @bcody why shouldnt it? You are trying to cast `Runnable` to `Serializable`. No different outcome possible – Antoniossss Aug 31 '16 at 18:13
  • @cypressious From the Serializable docs: _If a serializable class does not explicitly declare a serialVersionUID, then the **serialization runtime** will calculate a default serialVersionUID value for that class based on various aspects of the class, as described in the Java(TM) Object Serialization Specification._ – Nico Van Belle Mar 29 '17 at 06:54
  • @StuartMarks "**you have to create a new subinterface that extends all of them**", that's why it is useful, much like mixin. – Yankai Zhang Apr 08 '19 at 06:28
  • `(Runnable & Serializable) () -> System.exit(0);` bye – cambunctious Jul 16 '19 at 18:02
  • 1
    If you use `var` instead of an explicit type, you can use the object both as `Runnable` and `Serializable` without having to cast, for example when calling a method expecting a `Serializable` argument. More on this here: https://blog.codefx.org/java/intersection-types-var/ – Martin Aug 27 '19 at 14:07
29

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));
Pascal
  • 405
  • 4
  • 6
  • 4
    I like this answer because then any external caller you don't write will automatically also be serializable. If you want objects submitted to be serializable, your interface should be serializable, which is kind of the point of an interface. However, the question did say "without creating a `SerializableRunnable` 'dummy' interface" – slevin Nov 01 '17 at 20:42
25

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.

Vicente Romero
  • 1,460
  • 13
  • 16
8

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.

Rafaël
  • 977
  • 8
  • 17
4

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

ruediste
  • 2,434
  • 1
  • 21
  • 30
  • 1
    The post was moved here: https://ruediste.github.io/java/kryo/2017/05/07/serializing-non-serializable-lambdas.html – Danon Sep 19 '20 at 20:48