如何使用最内层范围内的所有可选值链接lambda,而不嵌套Optional#ifPresent()? [英] How to chain lambdas with all optional values available at the innermost scope without nesting Optional#ifPresent()?

查看:217
本文介绍了如何使用最内层范围内的所有可选值链接lambda,而不嵌套Optional#ifPresent()?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的另一个问题的分支:如何在没有嵌套的情况下在lambda中链接可选#ifPresent()?

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()?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆