序列化 lambda 在许多用例中都很有用,例如持久化配置,或作为远程资源的 访问者模式 。
远程访客
例如,假设我想访问远程地图上的资源。我可以使用 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 可序列化的一种简单方法是将 & Serializable 的强制转换添加到引用 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 的一个关键原因是避免样板代码,那么有什么选择呢?
在您的 API 中使 Lambda 可序列化
不幸的是,标准 API 无法更改或子类来添加它,但如果您有自己的 API,则可以使用 Serializable 接口。
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);
该接口可以用作参数类型。
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);
您的 API 的用户不必明确说明 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 应用于整个地图的方法。
查询与订阅
为了支持查询,如果您想隐式添加可序列化,则不能使用内置的 stream() API。但是,您可以创建一个尽可能相似的。
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);
或作为过滤订阅。
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);
这与常规流 API 的不同之处在于,数据可以分布在许多服务器上,并且当任何服务器上的数据发生变化时,您都会收到回调。当在服务器上应用过滤器和地图时,只有您感兴趣的数据会通过网络发送。
Java序列化
Java Serialization 是一个很好的通用的、向后兼容的序列化库。替代方案试图解决的两个最常见的问题是性能和跨平台序列化。
在上面的示例中,fullNameFunc 序列化为超过 700 个字节,并且优化它以减少消息大小或它产生的垃圾量的选项非常有限。相比之下,直接的二进制 YAML 序列化使用 348,具有更多优化序列化的选项。
这就提出了如何使用替代的、跨平台的或更快的序列化格式来序列化 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 的内容。要么看看它调用了什么方法,要么序列化它。在反序列化方面,您可以重新创建此对象并可以对该对象进行 readResolve。
标准API
目前,没有用于自省 lambda 的标准 API。这是故意这样做的,以便将来可以更改实现,尽管没有公共 JEP 可以这样做。然而,就像作为内部 API 的 Unsafe 一样,我期待有一天我们可以使用标准 API 而不是必须深入 JVM 的内部来实现解决方案。
结论
通过对 API 进行一些更改,您可以使序列化 lambda 对开发人员在很大程度上是透明的。这使得实现简单的分布式系统更容易使用,同时为您提供了优化其完成方式的选项。