如何在不嵌套Optional#ifPresent()的情况下将lambda与所有在内部范围内可用的可选值链接在一起?

Zhr*_*hro 5 java lambda optional

这是我另一个问题的分支:如何在lambda中链接Optional#ifPresent()而不进行嵌套?

但是,现在的问题是如何提供一个lambda解决方案,其中所有可选值都在最内部作用域中可用:

B b = procA().flatMap(this::procB).orElseThrow(SomeException::new);

// Value from procA() is not available.
Run Code Online (Sandbox Code Playgroud)

我的原始代码是:

void SomeMethod() {
    procA().ifPresent(a -> {
       procB(a).ifPresent(b -> {
          // Do something with a and b

          return;
       });
    });

    throw new SomeException();
}
Run Code Online (Sandbox Code Playgroud)

我了解return最内在的范围是错误的。新flatMap示例说明了正确的行为。

我使用ifPresent()而不是get()避免潜在的运行时异常,在这种情况下,我可能无法检查是否为optional的值isPresent()

Mal*_*wig 1

我发现这个问题非常有趣,因为链式调用具有潜力null是一种常见的麻烦,而可选可以大大缩短通常的空检查链。但存在的问题是函数流方法的本质隐藏了映射函数中的中间值。嵌套是一种保持它们可用的方法,但正如您所意识到的,如果调用链的长度增加,也会变得烦人。

我想不出一个简单且轻量级的解决方案,但如果您的项目的性质经常导致这些情况,这个 util 类可以提供帮助:

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以通过包装可选值或普通值来使用它。然后,当您使用该map方法来链接方法调用时,它将提供一个新的 ChainedOptional,同时将当前值存储在列表中。在最后(ifPresent, orElseThrow),您不仅会得到最后一个值,还会得到所有中间值的列表。由于不知道有多少调用将被链接,所以我没有找到一种以类型安全的方式存储这些值的方法。

请参阅此处的示例:

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
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助。如果您想出更好的解决方案,请告诉我。