hug*_*vey 54 java optional java-8
我有一些使用Java Optionals的(简化)代码:
Optional<User> maybeTarget = userRepository.findById(id1);
Optional<String> maybeSourceName = userRepository.findById(id2).map(User::getName);
Optional<String> maybeEventName = eventRepository.findById(id3).map(Event::getName);
maybeTarget.ifPresent(target -> {
maybeSourceName.ifPresent(sourceName -> {
maybeEventName.ifPresent(eventName -> {
sendInvite(target.getEmail(), String.format("Hi %s, $s has invited you to $s", target.getName(), sourceName, meetingName));
}
}
}
Run Code Online (Sandbox Code Playgroud)
不用说,这看起来很糟糕.但我想不出另一种以较少嵌套和更易读的方式做到这一点的方法.我考虑过流式传输3个选项,但放弃了这个想法,因为做了.filter(Optional::isPresent)
一个.map(Optional::get)
感觉甚至更糟.
那么是否有更好的,更"Java 8"或"可选识字"的方式来处理这种情况(基本上需要多个Optionals来计算最终操作)?
Sha*_*her 53
我认为流三者Optional
是一种矫枉过正,为什么不简单
if (maybeTarget.isPresent() && maybeSourceName.isPresent() && maybeEventName.isPresent()) {
...
}
Run Code Online (Sandbox Code Playgroud)
在我看来,与使用流API相比,这更清楚地说明了条件逻辑.
Jor*_*nee 29
使用辅助函数,事情至少会变得非嵌套:
@FunctionalInterface
interface TriConsumer<T, U, S> {
void accept(T t, U u, S s);
}
public static <T, U, S> void allOf(Optional<T> o1, Optional<U> o2, Optional<S> o3,
TriConsumer<T, U, S> consumer) {
o1.ifPresent(t -> o2.ifPresent(u -> o3.ifPresent(s -> consumer.accept(t, u, s))));
}
Run Code Online (Sandbox Code Playgroud)
allOf(maybeTarget, maybeSourceName, maybeEventName,
(target, sourceName, eventName) -> {
/// ...
});
Run Code Online (Sandbox Code Playgroud)
显而易见的缺点是,对于每个不同数量的Optional
s ,您需要单独的辅助函数重载
Stu*_*rks 28
由于原始代码正在执行其副作用(发送电子邮件),而不是提取或生成值,嵌套ifPresent
调用似乎是合适的.原始代码似乎并不太糟糕,事实上它似乎比已经提出的一些答案更好.但是,语句lambdas和类型的局部变量Optional
似乎增加了相当多的混乱.
首先,我将冒昧地修改原始代码,方法是将它包装在一个方法中,为参数提供好的名称,并组成一些类型名称.我不知道实际代码是否是这样的,但这对任何人来说都不应该令人惊讶.
// original version, slightly modified
void inviteById(UserId targetId, UserId sourceId, EventId eventId) {
Optional<User> maybeTarget = userRepository.findById(targetId);
Optional<String> maybeSourceName = userRepository.findById(sourceId).map(User::getName);
Optional<String> maybeEventName = eventRepository.findById(eventId).map(Event::getName);
maybeTarget.ifPresent(target -> {
maybeSourceName.ifPresent(sourceName -> {
maybeEventName.ifPresent(eventName -> {
sendInvite(target.getEmail(), String.format("Hi %s, %s has invited you to %s",
target.getName(), sourceName, eventName));
});
});
});
}
Run Code Online (Sandbox Code Playgroud)
我玩了不同的重构,我发现将内部语句lambda提取到自己的方法对我来说是最有意义的.给定源和目标用户以及一个事件 - 没有可选的东西 - 它会发送有关它的邮件.这是在处理完所有可选内容后需要执行的计算.我还在这里移动了数据提取(电子邮件,名称),而不是将其与外层中的Optional处理混合.同样,这对我来说很有意义:从源到目标发送关于事件的邮件.
void setupInvite(User target, User source, Event event) {
sendInvite(target.getEmail(), String.format("Hi %s, %s has invited you to %s",
target.getName(), source.getName(), event.getName()));
}
Run Code Online (Sandbox Code Playgroud)
现在,让我们来处理可选的东西.正如我上面所说,ifPresent
是这样的方式,因为我们想要做副作用的事情.它还提供了一种从Optional中"提取"值并将其绑定到名称的方法,但仅限于lambda表达式的上下文中.由于我们希望为三个不同的Optional执行此操作,因此需要嵌套.嵌套允许外部lambda的名称被内部lambda捕获.这使我们可以将名称绑定到从Optionals中提取的值 - 但前提是它们存在.这不能用线性链完成,因为像元组这样的中间数据结构对于构建部分结果是必要的.
最后,在最里面的lambda中,我们调用上面定义的helper方法.
void inviteById(UserId targetId, UserId sourceID, EventId eventId) {
userRepository.findById(targetId).ifPresent(
target -> userRepository.findById(sourceID).ifPresent(
source -> eventRepository.findById(eventId).ifPresent(
event -> setupInvite(target, source, event))));
}
Run Code Online (Sandbox Code Playgroud)
请注意,我已经内联了Optionals而不是将它们保存在局部变量中.这揭示了嵌套结构更好一点.如果其中一个查找没有找到任何内容,它还提供操作的"短路",因为ifPresent
在空的Optional上什么都不做.
不过,我的眼睛仍然有点密集.我认为原因是这段代码仍然依赖于一些外部存储库来进行查找.将它与Optional处理混合在一起有点不舒服.一种可能性是简单的查找提取到自己的方法findUser
和findEvent
.这些很明显,所以我不会把它们写出来.但如果这样做,结果将是:
void inviteById(UserId targetId, UserId sourceID, EventId eventId) {
findUser(targetId).ifPresent(
target -> findUser(sourceID).ifPresent(
source -> findEvent(eventId).ifPresent(
event -> setupInvite(target, source, event))));
}
Run Code Online (Sandbox Code Playgroud)
从根本上说,这与原始代码没有什么不同.这是主观的,但我认为我更喜欢原始代码.它具有相同的,相当简单的结构,虽然嵌套而不是典型的可选处理线性链.不同的是,查找是在可选处理中有条件地完成的,而不是预先完成,存储在局部变量中,然后仅对可选值进行条件提取.此外,我已将数据操作(提取电子邮件和名称,发送消息)分离为单独的方法.这避免了将数据操作与可选处理混合在一起,如果我们处理多个Optional实例,我认为这会使事情变得混乱.
pvp*_*ran 25
这样的事情怎么样?
if(Stream.of(maybeTarget, maybeSourceName,
maybeEventName).allMatch(Optional::isPresent))
{
sendinvite(....)// do get on all optionals.
}
Run Code Online (Sandbox Code Playgroud)
话说回来.如果你在数据库中查找的逻辑只是发送邮件,那么如果maybeTarget.ifPresent()
是false,那么没有必要获取其他两个值,不是吗?我担心,这种逻辑只能通过传统的if else语句来实现.
Fed*_*ner 10
我认为你应该考虑采取另一种方法.
我首先不要在开始时向DB发出三个调用.相反,我会发出第一个查询,只有当结果存在时,我才会发出第二个查询.然后,我将对第3个查询应用相同的基本原理,最后,如果最后的结果也存在,我将发送邀请.当前两个结果中的任何一个不存在时,这将避免对DB的不必要的调用.
为了使代码更具可读性,可测试性和可维护性,我还将每个数据库调用提取到自己的私有方法,并将它们链接起来Optional.ifPresent
:
public void sendInvite(Long targetId, Long sourceId, Long meetingId) {
userRepository.findById(targetId)
.ifPresent(target -> sendInvite(target, sourceId, meetingId));
}
private void sendInvite(User target, Long sourceId, Long meetingId) {
userRepository.findById(sourceId)
.map(User::getName)
.ifPresent(sourceName -> sendInvite(target, sourceName, meetingId));
}
private void sendInvite(User target, String sourceName, Long meetingId) {
eventRepository.findById(meetingId)
.map(Event::getName)
.ifPresent(meetingName -> sendInvite(target, sourceName, meetingName));
}
private void sendInvite(User target, String sourceName, String meetingName) {
String contents = String.format(
"Hi %s, $s has invited you to $s",
target.getName(),
sourceName,
meetingName);
sendInvite(target.getEmail(), contents);
}
Run Code Online (Sandbox Code Playgroud)
第一种方法并不完美(它不支持懒惰 - 无论如何都将触发所有3个数据库调用):
Optional<User> target = userRepository.findById(id1);
Optional<String> sourceName = userRepository.findById(id2).map(User::getName);
Optional<String> eventName = eventRepository.findById(id3).map(Event::getName);
if (Stream.of(target, sourceName, eventName).anyMatch(obj -> !obj.isPresent())) {
return;
}
sendInvite(target.get(), sourceName.get(), eventName.get());
Run Code Online (Sandbox Code Playgroud)
以下示例有点冗长,但它支持懒惰和可读性:
private void sendIfValid() {
Optional<User> target = userRepository.findById(id1);
if (!target.isPresent()) {
return;
}
Optional<String> sourceName = userRepository.findById(id2).map(User::getName);
if (!sourceName.isPresent()) {
return;
}
Optional<String> eventName = eventRepository.findById(id3).map(Event::getName);
if (!eventName.isPresent()) {
return;
}
sendInvite(target.get(), sourceName.get(), eventName.get());
}
private void sendInvite(User target, String sourceName, String eventName) {
// ...
}
Run Code Online (Sandbox Code Playgroud)
如果您想坚持Optional
并且不承诺立即使用该值,则可以使用以下内容.它使用Triple<L, M, R>
Apache Commons:
/**
* Returns an optional contained a triple if all arguments are present,
* otherwise an absent optional
*/
public static <L, M, R> Optional<Triple<L, M, R>> product(Optional<L> left,
Optional<M> middle, Optional<R> right) {
return left.flatMap(l -> middle.flatMap(m -> right.map(r -> Triple.of(l, m, r))));
}
// Used as
product(maybeTarget, maybeSourceName, maybeEventName).ifPresent(this::sendInvite);
Run Code Online (Sandbox Code Playgroud)
可以想象两个或多个Optional
s 的类似方法,尽管java遗憾的是没有一般的元组类型(还).
归档时间: |
|
查看次数: |
10802 次 |
最近记录: |