I have a wicket project with lambda expressions. On one page when user clicks on back button my application crashes on:
java.lang.IllegalArgumentException: Invalid lambda deserialization
at x.y.z.MyPage$3.$deserializeLambda$(MyPage.java:1)
In the page class (where I return) I'm using lambda expression for implementation of this interface:
public interface Localizator extends Serializable {
String getLocalizedString(String key);
}
And the lambda:
protected void someMethod() {
localize((String key) -> getString(key));
}
When I change lambda to anonymous class, everything works fine. How should I use lambda in this case?
Env: Java 1.8.0_25, Netbeans 8.0.2, Wicket 6.17.0.
EDIT: This is real (but simplified) method with lambda:
@Override
protected DataLoader createDataLoader() {
return new DataLoader(){
@Override
public List loadData() {
...
}
@Override
public List convertToTableRows(List data) {
return Converter.asRowList(
data,
(Record record) -> {...}, // this lambda is OK
(String key) -> getString(key)); // this lambda is crashing
}
@Override
public List filterTableRow() {
...
}
};
}
Converter class:
public class Converter implements Serializable {
public static ArrayList asRowList(List data, OtherLoader loader, Localizator localizator){...}
DataLoader also extends Serializable.
To enable Serialization
support, having an interface
extending Serializable
is sufficient. There is no need to do an additional cast then. This is indicated by the fact that your code could serialize the lambda expression instance. It failed when deserializing, which is an indicator for a class incompatibility, so if inserting the cast helped, it’s very likely, that it was a side effect because inserting the cast forced recompiling of the class containing the lambda expression.
If you want to continue using serialized lambdas, you should understand how it works, as explained in the documentation of 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 when the $deserializeLambda$
rejects the deserialization with an exception, it implies that the properties of the serialized lambda do not match the properties of a known serializable lambda defined inside the class. These properties may turn out to be very fragile.
They include the functional interface, the captured arguments and a target method. In case of a lambda expression (rather than a method reference), the target method is a synthetic method inside the defining class with a compiler-chosen, unspecified name that might depend even on other lambda expressions within the same class as inserting another expression might cause a name change due to a numbering scheme.
Unlike ordinary classes where inserting a serialVersionUID
might tell the Serialization framework that it shouldn’t care about apparent inconsistencies, you can’t tell lambda expressions to show more robustness. In other words, once you have serialized lambda instances which ought to persist, you must not change the their defining class. See also this answer
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