7

In our Web application project, we are using the Redis to manage the session. To support it, we are serializing any object which will be stored in the session.

For example, we use DTO's to hold the bean data which is used to display on the screen. Even if the DTO having any other object inside (Composition) we also have to serialize it otherwise, we get NotSerializableException.

I had a problem when I was creating an anonymous inner class to implement the Comparator like below:

Collections.sort(people, new Comparator<Person>() {
    public int compare(Person p1, Person p2) {
        return p1.getLastName().compareTo(p2.getLastName());
    }
});

The above code threw the NotSerializableException and I resolved it by creating a class that implements the Comparator as well as Serializable interface. The problem was, it was thrown inside the JSP page which was using this DTO. I had to do a lot of debugging to find the actual problem.

But now, I'm wondering to change the above code to use Lambda expression like below:

Collections.sort(people, (p1, p2) -> p1.getLastName().compareTo(p2.getLastName()));

However, I fear the same exception might occur. Does the Lambda expression create objects internally?

VPK
  • 3,010
  • 1
  • 28
  • 35
  • Is [this](http://stackoverflow.com/questions/22807912/how-to-serialize-a-lambda?rq=1) what you mean? E.g. `Comparator c = (Comparator & Serializable) (p1, p2) -> p1.getLastName().compareTo(p2.getLastName())` – SME_Dev Jan 18 '17 at 11:24
  • Sorry, I don't know Lambda's that much, but does the above code means it will create an object which implements `Comparator` and `Serializable` interfaces internally? – VPK Jan 18 '17 at 11:28
  • Yes I think so, but I haven't tested it so far. I'm curios, so I'll check myself. – SME_Dev Jan 18 '17 at 11:29

3 Answers3

6

You can create a serializable lambda expression via

Collections.sort(people, (Comparator<Person>&Serializable)
    (p1, p2) -> p1.getLastName().compareTo(p2.getLastName()));

but it should be noted, that creating a Comparator via

(p1, p2) -> p1.getLastName().compareTo(p2.getLastName())

bears a discouraged redundancy. You are calling getLastName() twice and have to care to invoke it on the right parameter variable in either case. It is more straight-forward to use

Comparator.comparing(Person::getLastName)

instead. You can also make this comparator serializable, though that implies loosing much of the conciseness:

Collections.sort(people,
    Comparator.comparing((Function<Person,String>&Serializable)Person::getLastName));

This is also more robust. The serialized form of a lambda expression contains a reference to the implementation method, which is in the first variant a synthetic method with a compiler generated name that might change when you use another lambda expression within the defining method. In contrast, Person::getLastName points the the named method getLastName as implementation method (at least with javac).

But generally, serializable lambda expressions might contain surprising compiler dependencies and should be used with care.

Since they are meant to describe behavior rather than data, there is no point in long-term storage of them anyway. For transferring them between JVMs with the same code base, they are sufficient.

Holger
  • 285,553
  • 42
  • 434
  • 765
1

lambda

Yes it does create an Object. The second parameter is of type Comparator, so that's what will be created implicitly.

See this: Does a lambda expression create an object on the heap every time it's executed?


Embedded Redis

On a side note, you'd benefit in allowing your application to run locally. To start an embedded Redis server, you may take a look at my personal project on github: https://github.com/alexbt/sample-spring-boot-data-redis-embedded.

It's using the following dependencies:

<dependency>
    <groupId>com.github.kstyrc</groupId>
    <artifactId>embedded-redis</artifactId>
    <version>0.6</version>
</dependency>

and mostly these 3 lines to handle it:

new RedisServer(redisPort);
redisServer.start();
redisServer.stop();

You could even do that in a side project, just to stop/start Redis on a specific port. It would be even better if you integrate it inside the application trigger with a specific a Spring Profile, but that will be more work in the short term.


The library's website: https://github.com/kstyrc/embedded-redis:

Running RedisServer is as simple as:

RedisServer redisServer = new RedisServer(6379);
redisServer.start();
// do some work
redisServer.stop();
Community
  • 1
  • 1
alexbt
  • 16,415
  • 6
  • 78
  • 87
  • The problem in trying it is we do not have Redis environment on our local machine. Secondly, If it is going to create an object implicitly, then it might throw the `NotSerializableException` on the production environment, which would be very bad me :( – VPK Jan 18 '17 at 11:25
  • I'm pretty sure it works, BUT what you are saying is that it cannot be tested until production ?? Then, I wouldn't change a thing If I were you, unless you are willing to create a side project without external dependencies! – alexbt Jan 18 '17 at 11:27
  • 1
    On a side note, I have this project on github what showcases using an embedded Redis server.. In the long run, you'd benefits in allowing your application to run locally: https://github.com/alexbt/sample-spring-boot-data-redis-embedded – alexbt Jan 18 '17 at 11:29
0

If you are willing to switch to another serialization framework like Kryo, you can get rid of the multiple bounds or the requirement that the implemented interface must implement Serializable. Further you would not need to have all your serialized classes implement Serializable, generally have a lot more options to customize serialization, the serialized form would likely be smaller (saving memory) and the whole thing would likely be faster.

The approach to lambda serialization is to:

  1. Modify the InnerClassLambdaMetafactory to always generate the code required for serialization
  2. Directly call the LambdaMetaFactory during deserialization

For details and code see this blog post

VPK
  • 3,010
  • 1
  • 28
  • 35
ruediste
  • 2,434
  • 1
  • 21
  • 30