Como serializar um lambda?

Como posso elegantemente serializar um lambda?

Por exemplo, o código abaixo lança um NotSerializableException . Como posso corrigi-lo sem criar uma interface “dummy” 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(); } } 

O Java 8 introduz a possibilidade de converter um object em uma interseção de tipos, adicionando múltiplos limites . No caso de serialização, é portanto possível escrever:

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

E o lambda se torna automagicamente serializável.

A mesma construção pode ser usada para referências de método. Por exemplo, 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 uma expressão lambda e uma referência de método com um tipo de destino serializável.

Muito feio casting. Eu prefiro definir uma extensão serializável para a interface funcional que estou usando

Por exemplo:

 interface SerializableFunction extends Function, Serializable {} interface SerializableConsumer extends Consumer, Serializable {} 

então o método que aceita o lambda pode ser definido como tal:

 private void someFunction(SerializableFunction function) { ... } 

e chamando a function você pode passar o seu lambda sem qualquer feitio feio:

 someFunction(arg -> doXYZ(arg)); 

Se você estiver disposto a mudar para outro framework de serialização como o Kryo , você pode se livrar dos múltiplos limites ou do requisito que a interface implementada deve implementar Serializable . A abordagem é

  1. Modifique o InnerClassLambdaMetafactory para sempre gerar o código necessário para serialização
  2. Chame diretamente o LambdaMetaFactory durante a desserialização

Para detalhes e código, veja esta postagem no blog