使用以前链接的值,然后在Java 8中编写lambda

And*_*ong 6 java chaining java-8 completable-future

我的同事首选的Java 8编码风格一直在链接异步调用,例如

CompletionStage<E> someMethod() {
    return doSomething().thenCompose(a -> {
      // ...
      return b;
    }).thenCompose(b -> {
      // ...
      return c;
    }).thenCompose(c -> {
      // ...
      return d;
    }).thenApply(d -> {
      // ...
      return e;
    });
}
Run Code Online (Sandbox Code Playgroud)

我有类似上面的内容,但有一个额外的挑战:我需要回忆一些lambda中检索到的值,在后来的lambda中.例如,

CompletionStage<E> someMethod() {
    return doSomething().thenCompose(a -> {
      // ...
      Foo foo = fooDAO.getFoos(a);
      // ...
      return b;
    }).thenCompose(b -> {
      // ...
      return c;
    }).thenCompose(c -> {
      // ...
      Bar bar = barDAO.getBars(foo);
      // ...
      return d;
    }).thenApply(d -> {
      // ...
      return someResult(d, bar);
    });
}
Run Code Online (Sandbox Code Playgroud)

当我宣布Foo foo;Bar bar;在外部范围内时,我得到的错误是他们不是最终的或有效的最终.我读到了使用包装器使它们成为有效的最终版本,但对我来说这似乎相当骇人听闻(我不明白为什么允许这样做......)

我读到Java 8没有添加对元组的支持(尽管考虑过BiVal,也许我曾经习惯传递给BiFunctionlambdas).所以我尝试使用对,例如

    return doSomething().thenCompose(a -> {
      // ...
      Foo foo = fooDAO.getFoos(a);
      // ...
      return new Pair<>(foo, b);
    }).thenCompose(fooAndB -> {
Run Code Online (Sandbox Code Playgroud)

然后我需要回忆的地方foo,

      Foo foo = fooAndB.getKey();
Run Code Online (Sandbox Code Playgroud)

但这在语义上是错误的.此外,它不起作用!我不知道为什么,因为我认为lambda参数的范围与其外部范围相同,因此所有lambda参数都可以在以后链接的lambdas中访问.

lambda参数的范围究竟是什么,并且在保持链接的同时,是否存在惯用或至少语义上无害的方式来做我想做的事情?

基于打破链条的答案很好,因为它们可能对未来的观众有用,但在我的情况下,偏离此回购中的主导风格可能导致PR谈话和延迟批准,所以我喜欢保留的解决方案链接.或者,解释或演示如何保持链接是多么疯狂.谢谢!

Did*_*r L 5

既然你提到了同事喜欢编码风格,你可能已经知道使用嵌套调用的替代方法:

CompletionStage<E> someMethod() {
    return doSomething().thenCompose(a -> {
        // ...
        Foo foo = fooDAO.getFoos(a);
        // ...
        CompletableFuture<B> resultB = ...;
        return resultB.thenCompose(b -> {
            // ...
            CompletableFuture<C> resultC = ...;
            return resultC;
        }).thenCompose(c -> {
            // ...
            Bar bar = barDAO.getBars(foo);
            // ...
            CompletableFuture<D> resultD = ...;
            return resultD.thenApply(d -> {
                // ...
                return someResult(d, bar);
            });
        });
    });
}
Run Code Online (Sandbox Code Playgroud)

这会立即解决您的问题,但代价是代码可读性稍差。但是这个问题可以通过从你的代码中提取一些方法来轻松解决:

CompletionStage<E> someMethod() {
    return doSomething()
            .thenCompose(this::processA);
}

private CompletionStage<E> processA(final A a) {
    // ...
    Foo foo = fooDAO.getFoos(a);
    // ...
    final CompletableFuture<B> result = ...;
    return result
            .thenCompose(this::processB)
            .thenCompose(c -> processCAndFoo(c, foo));
}

private CompletionStage<C> processB(B b) {
    // ...
    return ...;
}

private CompletionStage<E> processCAndFoo(final C c, final Foo foo) {
    // ...
    Bar bar = barDAO.getBars(foo);
    // ...
    final CompletableFuture<D> result = ...;
    return result
            .thenApply(d -> someResult(d, bar));
}
Run Code Online (Sandbox Code Playgroud)

通过这样做,您可以避免嵌套 lambdas(并尊重您同事的首选代码风格),但您也获得了可读性和可测试性,因为您现在拥有几个更易于理解和单元测试的小方法。