如何以及为什么序列化Lambda

总览

lambda序列化在许多用例中很有用,例如持久配置或作为远程资源的访客模式

远程访客

例如,因此我想访问远程Map上的资源,可以使用get / put,但是说我只想从Map的值中返回一个字段,我可以将lambda作为访问者来传递以提取信息。我想要。

MapView userMap =
     Chassis.acquireMap("users", String.class, UserInfo.class);
userMap.put("userid", new UserInfo("User's Name"));

// print out changes

userInfo.registerSubscriber(System.out::println);

// obtain just the fullName without downloading the whole object
String name= userMap.applyToKey("userid", u -> u.fullName);

// increment a counter atomically and trigger
// an updated event printed with the subscriber.
userMap.asyncUpdateKey("userid", ui -> {
     ui.usageCounter++;
     return ui;
});

// increment a counter and return the userid
int count = userMap.syncUpdateKey("userid",
      ui -> { ui.usageCounter++; return ui;},
      ui -> ui.usageCounter);

如您所见,添加各种简单功能或调用方法来执行所需的操作很容易。 唯一的问题是默认情况下lambda无法序列化。

可序列化的Lambda

使lambda可序列化的一种简单方法是将&的可转换类型添加到引用lambda的实现的变量中。

Function<UserInfo, String> fullNameFunc = (Function<UserInfo,String> & Serializable) ui -> ui.fullName;
String fullName = userInfo.applyToKey("userid", fullNameFunc);

如您所见,这引入了很多样板。 使用lambda的一个关键原因是避免样板代码,那么替代方法是什么?

使lambda可在您的API中序列化。

不幸的是,无法更改标准API或添加其子类,但是如果您拥有自己的API,则可以使用Serializable接口。

@FunctionalInterface
public interface SerializableFunction<I, O> extends Function<I, O>, Serializable {
}

该接口可用作参数类型。

default <R> R applyToKey(K key, @NotNull SerializableFunction<E, R> function) {
    return function.apply(get(key));
}

您的API用户不必明确声明lambda是可序列化的。

// obtain just the fullName without downloading the whole object
String name= userMap.applyToKey("userid", u -> u.fullName);

远程实现对lambda进行序列化,然后在服务器上执行该lambda并返回结果。

类似地,存在将lambda应用于整个地图的方法。

查询和订阅

为了支持查询,如果要隐式添加Serializable,则不能使用内置的stream()API。 但是,您可以创建一个尽可能相似的文件。

Map> collect = userMap.entrySet().query()
    .filter(e -> e.getKey().matches("u*d"))
    .map(e -> e.getValue())
    .collect(Collectors.groupingBy(u -> u.usageCounter));

或作为过滤的订阅。

// print userid which have a usageCounter > 10 each time it is incremented.        userMap.entrySet().query()
        .filter(e -> e.getValue().usageCounter > 10)
        .map(e -> e.getKey())
        .subscribe(System.out::println);

这与常规流API的不同之处在于,数据可以分布在许多服务器上,并且当任何服务器上的数据发生更改时,您都会得到回调。 在服务器上应用过滤器和映射时,只有您感兴趣的数据才通过网络发送。

Java序列化

Java序列化是一个很好的通用化,向后兼容的序列化库。 替代方案尝试解决的两个最常见问题是性能和跨平台序列化。

在上面的示例中,fullNameFunc序列化到700多个字节,并且有非常有限的选项来优化它以减少消息的大小或产生的垃圾量。 相比之下,简单的二进制YAML序列化使用348,并提供更多选项来优化序列化。

这就提出了如何使用替代,跨平台或更快的序列化格式来序列化lambda的问题。

替代序列化

您可以加入当前的序列化机制。 不支持此功能,它可以随时更改,但是没有其他受支持的方式来执行此操作。

无论如何,您可以这样做:

Method writeReplace = lambda.getClass()
                                  .getDeclaredMethod("writeReplace");
writeReplace.setAccessible(true);
SerializedLambda sl = (SerializedLambda) writeReplace.invoke(lambda);

这为您提供了一个对象,您可以检查该对象以提取lambda的内容。 要么查看它调用什么方法,要么对其进行序列化。 在反序列化方面,您可以重新创建该对象并可以在该对象上读取Resolve。

标准API

当前,没有用于内省lambda的标准API。 这样做是有意进行的,以便将来可以更改实现,尽管没有公共JEP可以这样做。 但是,就像Unsafe是内部API一样,我期待有一天可以使用标准API,而不必深入研究JVM的内部来实现解决方案。

结论

通过对API进行一些更改,您可以使序列化lambda对开发人员而言基本上是透明的。 这使实现简单的分布式系统更易于使用,同时为您提供了优化方法。

翻译自: https://www.javacodegeeks.com/2015/07/how-and-why-to-serialize-lambdas.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值