如何使用Java8选项,在三个选项都存在的情况下执行操作?
我有一些使用Java选项的(简化)代码:如何使用Java8选项,在三个选项都存在的情况下执行操作?,java,java-8,optional,Java,Java 8,Optional,我有一些使用Java选项的(简化)代码: Optional<User> maybeTarget = userRepository.findById(id1); Optional<String> maybeSourceName = userRepository.findById(id2).map(User::getName); Optional<String> maybeEventName = eventRepository.findById(id3).map(
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));
}
}
}
可选的maybeTarget=userRepository.findById(id1);
可选的maybeSourceName=userRepository.findById(id2.map)(User::getName);
可选的maybeEventName=eventRepository.findById(id3.map)(Event::getName);
可能目标。如果存在(目标->{
可能是源名称。如果存在(sourceName->{
maybeEventName.ifPresent(事件名称->{
sendInvite(target.getEmail(),String.format(“您好%s,$s已邀请您访问$s”,target.getName(),sourceName,meetingName));
}
}
}
不用说,这看起来和感觉都很糟糕。但我想不出其他方法来以嵌套更少、可读性更高的方式来实现这一点。我考虑过将3个选项流式处理,但放弃了使用。过滤器(可选::isPresent)
的想法,然后使用。映射(可选::get)
感觉更糟
那么,有没有更好的、更“Java 8”或“可选读写”的方法来处理这种情况(基本上是多个选项都需要计算一个最终操作)?类似的方法如何
if(Stream.of(maybeTarget, maybeSourceName,
maybeEventName).allMatch(Optional::isPresent))
{
sendinvite(....)// do get on all optionals.
}
话虽如此。如果您在数据库中查找的逻辑只是发送邮件,那么如果
maybeTarget.ifPresent()
为false,那么获取其他两个值就没有意义了,不是吗?恐怕这种逻辑只能通过传统的If-else语句来实现。第一种方法并不完美(它不支持惰性-仍将触发所有3个数据库调用):
Optional target=userRepository.findById(id1);
可选的sourceName=userRepository.findById(id2.map)(User::getName);
可选的eventName=eventRepository.findById(id3.map)(Event::getName);
if(Stream.of(target,sourceName,eventName).anyMatch(obj->!obj.isPresent())){
返回;
}
sendInvite(target.get()、sourceName.get()、eventName.get());
下面的示例有点冗长,但它支持惰性和可读性:
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) {
// ...
}
private void sendiffalid(){
可选目标=userRepository.findById(id1);
如果(!target.isPresent()){
返回;
}
可选的sourceName=userRepository.findById(id2.map)(User::getName);
如果(!sourceName.isPresent()){
返回;
}
可选的eventName=eventRepository.findById(id3.map)(Event::getName);
如果(!eventName.isPresent()){
返回;
}
sendInvite(target.get()、sourceName.get()、eventName.get());
}
私有void sendInvite(用户目标、字符串sourceName、字符串eventName){
// ...
}
使用helper函数,事情至少有一点不嵌套:
@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))));
}
明显的缺点是,对于每一个不同数量的
可选的s您都需要一个单独的helper函数重载。我认为流式处理三个可选的s是一种过分的做法,为什么不简单呢
if (maybeTarget.isPresent() && maybeSourceName.isPresent() && maybeEventName.isPresent()) {
...
}
在我看来,这比使用流API更清楚地说明了条件逻辑。
< P>我认为你应该考虑采取另一种方法。
首先,我不会在开始时向DB发出这三个调用。相反,我会发出第一个查询,只有当结果存在时,我才会发出第二个查询。然后,我会对第三个查询应用相同的原理,最后,如果最后一个结果也存在,我会发送invite。这样可以避免在eit时向DB发出不必要的调用她的前两个结果之一不存在
为了使代码更具可读性、可测试性和可维护性,我还将每个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);
}
我采用了相同的方法,只在需要时调用DB,这也很冗长,但很懒惰。我还简化了一点。考虑到您有以下3种方法:
public static Optional<String> firstCall() {
System.out.println("first call");
return Optional.of("first");
}
public static Optional<String> secondCall() {
System.out.println("second call");
return Optional.empty();
}
public static Optional<String> thirdCall() {
System.out.println("third call");
return Optional.empty();
}
因为原始代码的副作用正在被执行(发送电子邮件),而不是提取或生成值,嵌套的ifPresent
调用似乎是合适的。原始代码似乎不太糟糕,实际上它似乎比已经提出的一些答案要好。但是,lambdas语句和Optional
类型的局部变量似乎确实添加了大量clutter
首先,我将自由地修改原始代码,将其包装在一个方法中,为参数指定好的名称,并生成一些类型名称。我不知道实际代码是否是这样的,但这对任何人来说都不应该感到惊讶
// 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));
});
});
});
}
现在,让我们来处理一些可选的东西。正如我上面所说的,ifPresent
是我们的方法,因为我们想做一些有副作用的事情。它还提供了一种“提取”的方法从一个Optional中获取值,并将其绑定到一个名称,但仅在lambda表达式的上下文中。由于我们希望对三个不同的Optional执行此操作,因此需要调用嵌套。嵌套允许外部lambda中的名称被内部lambda捕获。这允许我们将名称绑定到从Optional中提取的值,但前提是这些值存在。这实际上不能用线性链来完成,因为某些中间数据结构(如元组)是构建部分结果所必需的
最后,在最内层的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))));
}
请注意,我内联了选项,而不是将它们保存在局部变量中。这更好地揭示了嵌套结构。如果其中一个查找没有找到任何内容,它还提供了操作的“短路”,因为ifPresent
只是对空的可选内容不执行任何操作
不过,在我看来,它仍然有点密集。我认为原因是,该代码仍然依赖于一些外部存储库来进行查找。将其与可选处理混合在一起有点不舒服。一种可能是简单地将查找提取到它们自己的方法findUser
和findEvent中代码>。这些是
firstCall()
.flatMap(x -> secondCall().map(y -> Stream.of(x, y))
.flatMap(z -> thirdCall().map(n -> Stream.concat(z, Stream.of(n)))))
.ifPresent(st -> System.out.println(st.collect(Collectors.joining("|"))));
// 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));
});
});
});
}
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()));
}
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))));
}
void inviteById(UserId targetId, UserId sourceID, EventId eventId) {
findUser(targetId).ifPresent(
target -> findUser(sourceID).ifPresent(
source -> findEvent(eventId).ifPresent(
event -> setupInvite(target, source, event))));
}
/**
* 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);
User target = userRepository.findById(id1).orElse(null);
User source = userRepository.findById(id2).orElse(null);
Event event = eventRepository.findById(id3).orElse(null);
if (target != null && source != null && event != null) {
String message = String.format("Hi %s, %s has invited you to %s",
target.getName(), source.getName(), event.getName());
sendInvite(target.getEmail(), message);
}
return userRepository.findById(id)
.flatMap(target -> userRepository.findById(id2)
.map(User::getName)
.flatMap(sourceName -> eventRepository.findById(id3)
.map(Event::getName)
.map(eventName-> createInvite(target, sourceName, eventName))))
class OptionalSupplier {
private final Supplier<Optional<?>> optionalSupplier;
private Optional<?> evaluatedOptional = null;
public OptionalSupplier(Supplier<Optional<?>> supplier) {
this.optionalSupplier = supplier;
}
public Optional<?> getEvaluatedOptional() {
if (evaluatedOptional == null)
evaluatedOptional = optionalSupplier.get();
return evaluatedOptional;
}
}
class OptionalSemaphores {
private List<OptionalSupplier> optionalSuppliers;
private List<Object> results = null;
private boolean allPresent;
public OptionalSemaphores(Supplier<Optional<?>>... suppliers) {
optionalSuppliers = Stream.of(suppliers)
.map(OptionalSupplier::new)
.collect(Collectors.toList());
allPresent = optionalSuppliers.stream()
.map(OptionalSupplier::getEvaluatedOptional)
.allMatch(Optional::isPresent);
if (allPresent)
results = optionalSuppliers.stream()
.map(OptionalSupplier::getEvaluatedOptional)
.map(Optional::get)
.collect(Collectors.toList());
}
public boolean isAllPresent() {
return allPresent;
}
public <T> T execute(Function<List<Object>, T> function, T defaultValue) {
return (allPresent) ? function.apply(results) : defaultValue;
}
public void execute(Consumer<List<Object>> function) {
if (allPresent)
function.accept(results);
}
}
public class OptionalsTester {
public static void main(String[] args) {
Supplier<Optional<?>> s1 = () -> Optional.of("Hello");
Supplier<Optional<?>> s2 = () -> Optional.of(1L);
Supplier<Optional<?>> s3 = () -> Optional.of(55.87);
Supplier<Optional<?>> s4 = () -> Optional.of(true);
Supplier<Optional<?>> s5 = () -> Optional.of("World");
Supplier<Optional<?>> failure = () -> Optional.ofNullable(null);
Supplier<Optional<?>> s7 = () -> Optional.of(55);
System.out.print("\nFAILING SEMAPHORES: ");
new OptionalSemaphores(s1, s2, s3, s4, s5, failure, s7).execute(System.out::println);
System.out.print("\nSUCCESSFUL SEMAPHORES: ");
new OptionalSemaphores(s1, s2, s3, s4, s5, s7).execute(System.out::println);
}
static class OptionalSemaphores {
private List<OptionalSupplier> optionalSuppliers;
private List<Object> results = null;
private boolean allPresent;
public OptionalSemaphores(Supplier<Optional<?>>... suppliers) {
optionalSuppliers = Stream.of(suppliers)
.map(OptionalSupplier::new)
.collect(Collectors.toList());
allPresent = optionalSuppliers.stream()
.map(OptionalSupplier::getEvaluatedOptional)
.allMatch(Optional::isPresent);
if (allPresent)
results = optionalSuppliers.stream()
.map(OptionalSupplier::getEvaluatedOptional)
.map(Optional::get)
.collect(Collectors.toList());
}
public boolean isAllPresent() {
return allPresent;
}
public <T> T execute(Function<List<Object>, T> function, T defaultValue) {
return (allPresent) ? function.apply(results) : defaultValue;
}
public void execute(Consumer<List<Object>> function) {
if (allPresent)
function.accept(results);
}
}
static class OptionalSupplier {
private final Supplier<Optional<?>> optionalSupplier;
private Optional<?> evaluatedOptional = null;
public OptionalSupplier(Supplier<Optional<?>> supplier) {
this.optionalSupplier = supplier;
}
public Optional<?> getEvaluatedOptional() {
if (evaluatedOptional == null)
evaluatedOptional = optionalSupplier.get();
return evaluatedOptional;
}
}
}