问题描述
这是我的另一个问题的分支:
This is an offshoot of my other question: How to chain Optional#ifPresent() in lambda without nesting?
但是,现在的问题是如何提供一个lambda解决方案,其中所有可选值都在最里面的范围内可用:
However, the problem now is how to provide a lambda solution where all of the optional values are available at the innermost scope:
B b = procA().flatMap(this::procB).orElseThrow(SomeException::new);
// Value from procA() is not available.
我原来的代码是:
void SomeMethod() {
procA().ifPresent(a -> {
procB(a).ifPresent(b -> {
// Do something with a and b
return;
});
});
throw new SomeException();
}
我理解返回
在最里面的范围是错误的。新的 flatMap
示例说明了正确的行为。
I understand that the return
at the innermost scope is wrong. The new flatMap
example illustrates the correct behavior.
我正在使用 ifPresent()
而不是 get()
以避免潜在的运行时异常,我可能无法检查可选的值是否为isPresent()
。
I am using ifPresent()
instead of get()
to avoid potential runtime exceptions where I might fail to check whether the value of an optional isPresent()
.
推荐答案
我发现此问题非常有趣,因为链接的电话有潜在的 null
返回是一种常见的麻烦,而Optional可以大大缩短通常的空检查链。但问题是功能流方法的本质隐藏了映射函数中的中间值。嵌套是保持它们可用的一种方式,但是如果你已经意识到,如果调用链的长度增长,也会变得烦人。
I find this question very interesting as chained calls with potential null
returns are a common nuisance, and Optional can shorten the usual null check chain a lot. But the issue there is that the nature of the functional stream methods hides the intermediate values in the mapping functions. Nesting is a way to keep them available, but can also get annoying if the length of the call chain grows, as you have realized.
我想不出一个简单的轻量级解决方案,但如果项目的性质经常导致这些情况,则此util类可以提供帮助:
I cannot think of an easy and lightweight solution, but if the nature of your project leads to these situations regularly, this util class could help:
public static class ChainedOptional<T>
{
private final List<Object> intermediates;
private final Optional<T> delegate;
private ChainedOptional(List<Object> previousValues, Optional<T> delegate)
{
this.intermediates = new ArrayList<>(previousValues);
intermediates.add(delegate.orElse(null));
this.delegate = delegate;
}
public static <T> ChainedOptional<T> of(T value)
{
return of(Optional.ofNullable(value));
}
public static <T> ChainedOptional<T> of(Optional<T> delegate)
{
return new ChainedOptional<>(new ArrayList<>(), delegate);
}
public <R> ChainedOptional<R> map(Function<T, R> mapper)
{
return new ChainedOptional<>(intermediates, delegate.map(mapper));
}
public ChainedOptional<T> ifPresent(Consumer<T> consumer)
{
delegate.ifPresent(consumer);
return this;
}
public ChainedOptional<T> ifPresent(BiConsumer<List<Object>, T> consumer)
{
delegate.ifPresent(value -> consumer.accept(intermediates, value));
return this;
}
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)
throws X
{
return delegate.orElseThrow(exceptionSupplier);
}
public <X extends Throwable> T orElseThrow(Function<List<Object>, X> exceptionSupplier)
throws X
{
return orElseThrow(() -> exceptionSupplier.apply(intermediates));
}
}
您可以通过包装Optional或普通值来使用它。然后,当您使用 map
方法链接方法调用时,它将在列表中存储当前值时提供新的ChainedOptional。最后( ifPresent
, orElseThrow
),您不仅会获得最后一个值,还会获得所有值中间值。由于不知道将链接多少个调用,我找不到以类型安全的方式存储这些值的方法。
You use it by wrapping an Optional or a plain value. When you then use the map
method to chain method calls, it will provide a new ChainedOptional while storing the current value in a list. At the end (ifPresent
, orElseThrow
), you will not only get the last value, but also the list of all intermediate values. Since it is not known how many calls will be chained, I did not find a way to store those values in a type-safe way, though.
请参阅此处的示例:
ChainedOptional.of(1)
.map(s -> s + 1)
.map(s -> "hello world")
.map(s -> (String) null)
.map(String::length)
.ifPresent((intermediates, result) -> {
System.out.println(intermediates);
System.out.println("Result: " + result);
})
.orElseThrow(intermediates -> {
System.err.println(intermediates);
return new NoSuchElementException();
});
// [1, 2, hello world, null, null]
// Exception in thread "main" java.util.NoSuchElementException
// at ...
ChainedOptional.of(1)
.map(s -> s + 1)
.map(s -> "hello world")
// .map(s -> (String) null)
.map(String::length)
.ifPresent((intermediates, result) -> {
System.out.println(intermediates);
System.out.println("Result: " + result);
})
.orElseThrow(intermediates -> {
System.err.println(intermediates);
return new NoSuchElementException();
});
// [1, 2, hello world, 11]
// Result: 11
希望这会有所帮助。如果你想出一个更好的解决方案,请告诉我。
Hope this helps. Let me know if you come up with a nicer solution.
这篇关于如何使用最内层范围内的所有可选值链接lambda,而不嵌套Optional#ifPresent()?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!