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();
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 ofSerializedLambda
, rather than allowing default serialization to proceed.
SerializedLambda
has areadResolve
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 theSerializedLambda
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With