总览
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