How to link a lambda with all the optional values available in the innermost range without nesting optional#ifpresent()?

This is another branch of my question: how to chain optional #ifpresent() in lambda without nesting?

However, the problem now is how to provide a lambda solution in which all optional values are available in the innermost range:

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

// Value from procA() is not available.

My original code is:

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

          return;
       });
    });

    throw new SomeException();
}

I understand that the return in the innermost range is wrong The new flatmap example illustrates the correct behavior

I use ifpresent () instead of get () to avoid potential runtime exceptions. I may not be able to check the value of optional ispresent ()

Solution

I find this problem very interesting because chained calls with potentially null returns are a common problem, and optional can greatly shorten the usual null check chain But the problem is that the essence of the function flow method hides the intermediate value in the mapping function Nesting is a way to make them available, but if you already realize that if the length of the call chain increases, it will also become annoying

I can't think of a simple lightweight solution, but if the nature of the project often leads to these situations, this util class can 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));
    }
}

You can use it by wrapping optional or normal values Then, when you link a method call using the map method, it will provide a new chainedoptional when the current value is stored in the list Finally (ifpresent or else throw), you get not only the last value, but also a list of all intermediate values Because I don't know how many calls will be linked, I can't find a method to store these values in a type safe way

Look at the example here:

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,11]
// Result: 11

I hope this will help If you come up with a better solution, please let me know

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>