【问题标题】:How to serialize a lambda?如何序列化一个 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();
    }
}

【问题讨论】:

标签: java serialization lambda java-8


【解决方案1】:

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

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

详情和代码见blog post

【讨论】:

【解决方案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 是的,另请参阅:stackoverflow.com/questions/25391656/…
      猜你喜欢
      • 2019-03-10
      • 1970-01-01
      • 2021-07-28
      • 2011-02-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多