无法反序列化lambda

时间:2015-01-22 00:16:01

标签: java serialization lambda java-8

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

lambda声明如此

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

并使用由ObjectOutputStream包装的[n可选] BufferedOutputStream包装的FileOutputStream进行序列化而不会出现问题。但是,当[在不同的项目中]反序列化时,它会失败,说它无法找到包含序列化代码的封闭类。我已经尝试过各种各样的东西,比如将它们包装在一个可序列化的类中(用于测试目的的是用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();

2 个答案:

答案 0 :(得分:12)

如果没有定义它的类,则无法反序列化对象。这与lambda表达式没有改变。

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

请参阅SerializedLambda

  

可序列化lambda的实现者,例如编译器或语言运行库,应该确保实例正确地反序列化。一种方法是确保writeReplace方法返回SerializedLambda的实例,而不是允许默认序列化继续进行。

     

SerializedLambda有一个readResolve方法,用于在捕获类中查找名为$deserializeLambda$(SerializedLambda)的(可能是私有的)静态方法,并将其自身作为第一个参数调用,并返回结果。实现$deserializeLambda$的Lambda类负责验证SerializedLambda的属性是否与该类实际捕获的lambda一致。

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


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

this question中解释了有关可序列化lambda的含义的更多信息。

答案 1 :(得分:4)

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

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