如何序列化一个 lambda?

Posted

技术标签:

【中文标题】如何序列化一个 lambda?【英文标题】:How to serialize a lambda? 【发布时间】:2014-05-13 12:39:19 【问题描述】:

如何优雅地序列化 lambda?

例如,下面的代码会抛出 NotSerializableException。如何在不创建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();
    

【问题讨论】:

虽然这是可能的(请参阅所选答案),但实际上每个人都应该三思而后行。正式名称为"strongly discouraged",可以有serioussecurityimplications。 【参考方案1】:

如果你愿意切换到另一个像Kryo这样的序列化框架,你可以摆脱多重界限或者实现的接口必须实现Serializable的要求。方法是

    修改InnerClassLambdaMetafactory以始终生成序列化所需的代码 反序列化时直接调用LambdaMetaFactory

详情和代码见blog post

【讨论】:

帖子被移到这里:ruediste.github.io/java/kryo/2017/05/07/…【参考方案2】:

如果有人在创建 Beam/Dataflow 代码时掉到这里:

Beam 有自己的SerializableFunction 接口,因此不需要虚拟接口或冗长的强制转换。

【讨论】:

【参考方案3】:

非常丑陋的演员阵容。我更喜欢为我正在使用的功能接口定义一个 Serializable 扩展

例如:

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

那么接受 lambda 的方法可以这样定义:

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

并调用该函数,您可以传递您的 lambda,而无需任何丑陋的演员:

someFunction(arg -> doXYZ(arg));

【讨论】:

我喜欢这个答案,因为这样你不写的任何外部调用者也将自动被序列化。如果您希望提交的对象可序列化,则您的接口应该是可序列化的,这是接口的一种重点。但是,问题确实说“没有创建SerializableRunnable'dummy'接口”【参考方案4】:

同样的结构可以用于方法引用。例如这段代码:

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

定义一个 lambda 表达式和一个具有可序列化目标类型的方法引用。

【讨论】:

【参考方案5】:

Java 8 引入了cast an object to an intersection of types by adding multiple bounds 的可能性。在序列化的情况下,因此可以这样写:

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

并且 lambda 会自动变为可序列化。

【讨论】:

非常有趣——这个功能似乎相当强大。在转换 lambda 之外是否有使用这种转换表达式?例如。现在是否也可以用普通的匿名类做类似的事情? @Balder 添加了转换为交集类型的工具,以便为 lambda 的类型推断提供目标类型。由于 AIC 具有清单类型(即,其类型未被推断),因此将 AIC 转换为交集类型是没有用的。 (有可能,只是没用。)要让 AIC 实现多个接口,您必须创建一个扩展所有接口的新子接口,然后实例化它。 这会产生编译器警告,说没有定义serialVersionUID吗? 注意:这仅在您在施工期间应用演员表时有效。以下将抛出 ClassCastException: Runnable r = () -> System.out.println("Serializable!");可运行可序列化R = (可运行&可序列化)r; @bcody 是的,另请参阅:***.com/questions/25391656/…

以上是关于如何序列化一个 lambda?的主要内容,如果未能解决你的问题,请参考以下文章

Java 可序列化和 lambda 表达式

Python面试必考重点之列表,元组和字典第十二关——如果列表元素是字典序列,如何利用lambda表达式对列表进行升序降序排列

java成神之——properties,lambda表达式,序列化

[python](windows)分布式进程问题:pickle模块不能序列化lambda函数

C语言枚举序列问题

lambda-基于谓词筛选值序列