繁体   English   中英

带有序列化的 Java 8 Lambda 表达式

[英]Java 8 Lambda expression with Serialization

在我们的 Web 应用程序项目中,我们使用 Redis 来管理会话。 为了支持它,我们正在序列化将存储在会话中的任何对象。

例如,我们使用 DTO 来保存用于在屏幕上显示的 bean 数据。 即使 DTO 内部有任何其他对象(组合),我们也必须对其进行序列化,否则我们会得到NotSerializableException

当我创建一个匿名内部类来实现Comparator时遇到了一个问题,如下所示:

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

上面的代码抛出了NotSerializableException ,我通过创建一个实现ComparatorSerializable接口的类来解决它。 问题是,它被抛出到使用这个 DTO 的JSP页面中。 我不得不进行大量调试才能找到实际问题。

但是现在,我想将上面的代码更改为使用如下所示的 Lambda 表达式:

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

但是,我担心可能会发生同样的异常。 Lambda 表达式是否在内部创建对象?

您可以通过以下方式创建可序列化的 lambda 表达式

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

但应该注意的是,通过创建Comparator

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

带有不鼓励的冗余。 您正在调用getLastName()两次,并且在任何一种情况下都必须注意在正确的参数变量上调用它。 使用起来更直接

Comparator.comparing(Person::getLastName)

反而。 您还可以使这个比较器可序列化,尽管这意味着失去了大部分简洁性:

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

这也更健壮。 lambda 表达式的序列化形式包含对实现方法的引用,在第一个变体中,它是一个合成方法,具有编译器生成的名称,当您在定义方法中使用另一个 lambda 表达式时,该名称可能会更改。 相比之下, Person::getLastName点的命名方法getLastName为实现方法(至少javac )。

但通常,可序列化的 lambda 表达式可能包含令人惊讶的编译器依赖项,应谨慎使用。

由于它们旨在描述行为而不是数据,因此无论如何长期存储它们是没有意义的。 对于在具有相同代码库的 JVM 之间传输它们,它们就足够了。

拉姆达

是的,它确实创建了一个对象。 第二个参数是 Comparator 类型,所以这就是隐式创建的。

看到这个: lambda 表达式每次执行时都会在堆上创建一个对象吗?


嵌入式Redis

附带说明一下,允许您的应用程序在本地运行会使您受益。 要启动嵌入式Redis服务器,您可以查看我在github上的个人项目: https : //github.com/alexbt/sample-spring-boot-data-redis-embedded

它使用以下依赖项:

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

主要是这 3 行来处理它:

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

您甚至可以在副项目中执行此操作,只需在特定端口上停止/启动 Redis。 如果您将它集成到具有特定 Spring Profile 的应用程序触发器中会更好,但在短期内这将是更多工作。


图书馆网站: https : //github.com/kstyrc/embedded-redis

运行 RedisServer 非常简单:

 RedisServer redisServer = new RedisServer(6379); redisServer.start(); // do some work redisServer.stop();

如果您愿意切换到另一个像Kryo这样的序列化框架,您可以摆脱多重边界或实现的接口必须实现Serializable 此外,您不需要让所有序列化类都实现Serializable ,通常有更多选项来自定义序列化,序列化形式可能会更小(节省内存),整个过程可能会更快。

lambda 序列化的方法是:

  1. 修改InnerClassLambdaMetafactory始终生成序列化所需的代码
  2. 反序列化时直接调用LambdaMetaFactory

有关详细信息和代码,请参阅此博客文章

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM