无法反序列化 lambda

Unable to deserialize lambda

作为一个小项目,我一直在尝试做一个小东西来读取序列化的 lambda(本地或从 FTP)并调用它们的 运行 函数作为测试的一部分在 Windows 中试验文件关联(即打开某些文件类型会用某个程序打开它们)等等,但无论我尝试什么,它似乎都无法正确反序列化。

lambda 是这样声明的

Runnable r = (Runnable & Serializable) () -> {
    // blah blah
    // made sure not to capture anything
};

并使用由 ObjectOutputStream 包装的 [n 可选] BufferedOutputStream 包装的 FileOutputStream 进行序列化,没有问题。然而,当[在不同的项目中]反序列化时,它失败了,说它找不到包含序列化它的代码的封闭 class 。我已经尝试过各种方法,比如将它们包装在可序列化的 class(w/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();

反序列化对象时,执行反序列化的代码必须知道序列化对象的 class。您不能序列化任意 lambda 并在另一个代码库中反序列化它。

或多或少,序列化和反序列化代码必须在同一个代码库中,或者至少必须共享对包含原始 lambda 的代码的依赖。

如果没有 class 定义,您不能反序列化一个对象。 lambda 表达式并没有改变这一点。

Lambda 表达式有点复杂,因为它们生成的运行时 class 不是定义它的 class 但它们的定义 class 是保存 lambda 主体代码的那个并且,在可序列化的 lambda 的情况下,反序列化支持方法被调用以验证和重新实例化 lambda 实例。

参见SerializedLambda

Implementors of serializable lambdas, such as compilers or language runtime libraries, are expected to ensure that instances deserialize properly. One means to do so is to ensure that the writeReplace method returns an instance of SerializedLambda, rather than allowing default serialization to proceed.

SerializedLambda has a readResolve method that looks for a (possibly private) static method called $deserializeLambda$(SerializedLambda) in the capturing class, invokes that with itself as the first argument, and returns the result. Lambda classes implementing $deserializeLambda$ are responsible for validating that the properties of the SerializedLambda are consistent with a lambda actually captured by that class.

因此,即使您的实例没有在定义 class 中引用合成方法(例如,在方法引用此 class 之外的方法的情况下),反序列化仍然需要$deserializeLambda$ 用于验证实例的正确性,有意。


关于序列化 lambda 的“最佳实践”,请记住 lambda 表达式封装 行为,而不是状态。存储行为总是意味着只存储某种引用并要求用于恢复它的代码实现关联的行为。如果您只是通过符号名称或只是存储来引用预期行为,那也会起作用,例如关联 enum 个值。

有关具有可序列化 lambda 的含义的更多信息,请参阅 this question