Lambda, will it serialize?
So I have been ponder an enhancement required on the Tyrus project that would allow a user to broadcast to a subset of client connected to a URL across a cluster of machines. There are various way of doing this; but since I was playing with JDK 8 this problem definitely looked like a nail.
To this end I created a simple unit test class that would take my filter, serialise it to disk, read it back and in then execute it. It had a instance field “VALUE” that we could use to reference directly or indirectly to find out what would cause the serialisation to fail.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.function.Predicate;
import org.junit.Test;
public class SerializablePredicateFilterTest {
public String VALUE = "Bob";
public interface SerializablePredicate<T> extends Predicate<T>, Serializable {
}
public <T> void filter(SerializablePredicate<T> sp, T value) throws IOException, ClassNotFoundException {
sp.getClass().isLocalClass();
File tempFile = File.createTempFile("labmda", "set");
try (ObjectOutput oo = new ObjectOutputStream(new FileOutputStream(tempFile))) {
oo.writeObject(sp);
}
try (ObjectInput oi = new ObjectInputStream(new FileInputStream(tempFile))) {
SerializablePredicate<T> p = (SerializablePredicate<T>) oi.readObject();
System.out.println(p.test(value));
}
}
}So just to calibrate lets make sure that an anonymous inner class will fail, because it will always contain a reference to enclosing object….
@Test(expected = NotSerializableException.class)
public void testAnonymousDirect() throws IOException, ClassNotFoundException {
String value = VALUE;
filter(new SerializablePredicate<String>() {
@Override
public boolean test(String t) {
return value.length() > t.length();
}
}, "Bob");
}The same is true for local classes, what you don’t use local classes?
@Test(expected = NotSerializableException.class)
public void testLocalClass() throws IOException, ClassNotFoundException {
class LocalPredicate implements SerializablePredicate<String> {
@Override
public boolean test(String t) {
// TODO Implement this method
return false;
}
}
filter(new LocalPredicate(), "Bobby");
}So a standalone class will of course work, in this case a nested class for convenience.
public static class LengthPredicate implements SerializablePredicate<String> {
private String value;
public LengthPredicate(String value) {
super();
this.value = value;
}
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
@Override
public boolean test(String t) {
// TODO Implement this method
return false;
}
}
@Test
public void testStaticInnerClass() throws IOException, ClassNotFoundException {
filter(new LengthPredicate(VALUE), "Bobby");
}So lets get down with JDK 8, it turns out that my first try also fails but it does confirm that the serialisation is quite happy to take a Lambda in general.
@Test(expected = NotSerializableException.class)
public void testLambdaDirect() throws IOException, ClassNotFoundException {
filter((String s) -> VALUE.length() > s.length(), "Bobby");
}A slight modification to copy the value into a effectively final attributes, and voila the lambda is now serialised and retrieved properly.
@Test
public void testLambdaInDirect() throws IOException, ClassNotFoundException {
String value = VALUE;
filter((String s) -> value.length() > s.length(), "Bobby");
}And of course if the value is a simple method parameter it also works fine.
@Test
public void testLambdaParameter() throws IOException, ClassNotFoundException {
invokeWithParameter(VALUE);
}
private void invokeWithParameter(String value) throws java.lang.ClassNotFoundException, java.io.IOException {
filter((String s) -> value.length() > s.length(), "Bobby");
}So the answer is yes, you can get it to serialise if you are a bit careful.


It’s also possible to serialize anonymous as long they do not access values of the outer this instance by nulling the reflected this$0 reference before transmission.