Unable to deserialize lambda
Asked Answered
G

2

8

Just as a small project, I've been trying to make a wee thing that reads serialized lambdas (locally or from an FTP) and invokes their run functions as part of a test to experiment with file associations in Windows (i.e. opening certain file types opens them with a certain program) and whatnot, but no matter what I try, it never seems to properly deserialize.

The lambda was declared like so

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

and serialized using a FileOutputStream wrapped by a[n optional] BufferedOutputStream wrapped by an ObjectOutputStream without issue. However, when deserialized [in a different project], it fails, saying that it could not find the enclosing class that contained the code for serializing it. I've tried various things like wrapping them in a serializable class (w/serialVersionUID = 0L for testing purposes) or defining a an interface that extends Runnable and Serializable, but to no avail.

Yes, I am aware that serializing lambdas isn't really good practice (or so we're told), but I'm not sure how to go about turning functions and subroutines into something I can store as a file or in an FTP. If this isn't even the right way at all, do tell.

Oh, I'm using Eclipse Luna of whatever the latest version is.

Edit:

Deserialized like so

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();
Grillwork answered 22/1, 2015 at 0:16 Comment(7)
Wait are you trying to serialize a Method?Goosegog
Method objects aren't serializable. I'm talking about a Runnable lambda.Grillwork
Can you show us your serialization/deserialization code?Inactive
Looks fishy to me but I'll upvote, the question is interesting.Goosegog
What might you mean by "fishy"?Grillwork
Have you tried with the Oracle JDK?Bullroarer
By fishy I meant I wasn't sure that was possible.Goosegog
D
13

You can’t deserialize an object without the class defining it. This hasn’t changed with lambda expressions.

Lambda expressions are a bit more complex as their generated runtime class is not the class which defined it but their defining class is the one holding the code of the lambda’s body and, in case of serializable lambdas, a deserialization support method which is called for validating and re- instantiating the lambda instance.

See 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.

So even if your instance was not referring to a synthetic method inside the defining class (e.g. in the case of a method reference to a method outside this class), deserialization still requires the $deserializeLambda$ for validating the correctness of the instance, intentionally.


Regarding the “good practice” of serializing lambdas, keep in mind that lambda expressions encapsulate behavior, not state. Storing behavior always implies storing just some kind of reference and requiring the code intended to restore it, to have implemented the associated behavior. That would work as well if you just referred to the intended behavior by a symbolic name or just stored, e.g. associated enum values.

More about the implications of having serializable lambdas is explained in this question.

Duggins answered 22/1, 2015 at 8:48 Comment(3)
What might be the "standard" way of storing state-independent subroutines, if any, instead of doing what I'm doing?Grillwork
There is no standard way as normally you won’t store routines but data. If you want to store behavior, you have to either, make the defining class available on the restoring site or really store code, either as byte code or as expression String which has to be parsed.Duggins
Which way is less of a bad practice? Also, for whichever one is the better way to go, how does one go about parsing Java statements/expressions at runtime or storing straight-up bytecode? I feel as if this isn't really good practice, but just wanting to learn ways to pass Java-specific instructions from program to program, process to process.Grillwork
E
4

When you deserialize an object, the code doing the deserialization must know about the class of the serialized object. You cannot serialize an arbitrary lambda and deserialize it in another codebase.

More or less, the serializing and deserializing code must be in the same codebase, or at least must share a dependency on the code containing the original lambda.

Encroach answered 22/1, 2015 at 4:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.