Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Lambda, Will it Serialize?

DZone's Guide to

Lambda, Will it Serialize?

· DevOps Zone
Free Resource

Download “The DevOps Journey - From Waterfall to Continuous Delivery” to learn learn about the importance of integrating automated testing into the DevOps workflow, brought to you in partnership with Sauce Labs.

So I have been pondering 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....

01. @Test(expected = NotSerializableException.class)
02. publicvoidtestAnonymousDirect() throwsIOException, ClassNotFoundException {
03.  
04.   String value = VALUE;
05.  
06.  
07.   filter(newSerializablePredicate<String>() {
08.  
09.     @Override
10.     publicbooleantest(String t) {
11.       returnvalue.length() > t.length();
12.     }
13.   }, "Bob");
14.  
15. }

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.

Discover how to optimize your DevOps workflows with our cloud-based automated testing infrastructure, brought to you in partnership with Sauce Labs

Topics:

Published at DZone with permission of Gerard Davison, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}