无法反序列化lambda表达式

8
作为一个小项目,我一直在尝试制作一个东西,它可以读取序列化的lambda(本地或来自FTP),并将它们的运行函数调用作为测试的一部分,以实验Windows中的文件关联(即打开某些文件类型会使用特定的程序)等等。但是无论我尝试什么,它似乎从未正确反序列化。 lambda表达式声明如下:
Runnable r = (Runnable & Serializable) () -> {
    // blah blah
    // made sure not to capture anything
};

使用FileOutputStream包装的BufferedOutputStream,再包装ObjectOutputStream进行序列化没有问题。但是,在反序列化时(在另一个项目中),它失败了,说找不到包含其序列化代码的封闭类。我尝试过各种方法,如将它们包装在可序列化类中(测试目的为serialVersionUID = 0L)或定义一个实现Runnable和Serializable接口的接口,但都无济于事。

是的,我知道序列化lambda并不是一个好的做法(或者我们被告知这样做),但我不确定如何将函数和子例程转换为可以存储为文件或在FTP中存储的内容。如果这根本不是正确的方式,请告诉我。

哦,我正在使用Eclipse Luna或最新版本。

编辑:

这样进行反序列化

File f = new File(somePath);
FileInputStream fish = new FileInputStream(f);
BufferedInputStream bos = new BufferedInputStream(fish); // not really necessary
ObjectInputStream ois = new ObjectInputStream(bos);
Runnable r = (Runnable) ois.readObject();
ois.close();
r.run();

你是在尝试序列化一个方法吗? - Angelo Alvisi
方法对象不可序列化。我说的是一个可运行的 lambda。 - Mona the Monad
1
你能展示一下你的序列化/反序列化代码吗? - Edwin Dalorzo
“Fishy” 是什么意思? - Mona the Monad
1
你尝试过使用Oracle JDK吗? - Brian Goetz
显示剩余2条评论
2个回答

13
你无法在没有定义类的情况下反序列化对象。使用lambda表达式也是如此。
Lambda表达式比较复杂,因为它们生成的运行时类不是定义它的类,而是包含lambda主体代码的类。对于可序列化的lambda,还有一个反序列化支持方法,用于验证和重新实例化lambda实例。
请参阅SerializedLambda
可序列化的lambda的实现者,例如编译器或语言运行时库,需要确保实例可以正确反序列化。其中一种方法是确保writeReplace方法返回SerializedLambda的实例,而不是允许默认序列化继续进行。 SerializedLambda有一个readResolve方法,它会在捕获类中查找名为$deserializeLambda$(SerializedLambda)的(可能是私有的)静态方法,并使用本身作为第一个参数调用该方法,并返回结果。实现$deserializeLambda$的Lambda类负责验证SerializedLambda的属性是否与该类实际捕获的Lambda一致。
因此,即使您的实例没有引用定义类内部的合成方法(例如在引用该类外部的方法的方法引用的情况下),反序列化仍需要$deserializeLambda$来验证实例的正确性,这是有意为之的。
关于序列化lambda表达式的“良好实践”,请记住,lambda表达式封装的是“行为”,而不是状态。存储行为总是意味着只存储某种引用,并要求旨在恢复它的代码已实现相关行为。如果您只通过符号名称引用所需行为或仅存储关联的枚举值,例如,那也可以正常工作。
有关具有可序列化lambda表达式的影响的更多信息,请参见此问题

如果有的话,存储与状态无关的子程序的“标准”方式可能是什么,而不是像我所做的那样? - Mona the Monad
1
通常情况下,您不会存储例程而是数据,因此没有标准方法可用。如果您想存储行为,则必须使定义类在还原站点上可用,或者真正存储代码,无论是作为字节码还是表达式字符串,这都需要进行解析。 - Holger
哪种方式不是一个不好的惯例?此外,对于更好的方式,如何在运行时解析Java语句/表达式或存储直接字节码?我觉得这并不是一个好的实践,但只是想学习将Java特定指令从程序传递到程序、进程到进程的方法。 - Mona the Monad

4

当你反序列化一个对象时,进行反序列化的代码必须知道序列化对象的类。你不能序列化任意的 lambda 表达式,并在另一个代码库中进行反序列化。

可以说,序列化和反序列化的代码必须在同一代码库中,或者至少必须共享原始 lambda 所在代码的依赖项。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接