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
,也许我曾经习惯传递给BiFunction
lambdas).所以我尝试使用对,例如
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谈话和延迟批准,所以我喜欢保留的解决方案链接.或者,解释或演示如何保持链接是多么疯狂.谢谢!
既然你提到了同事喜欢的编码风格,你可能已经知道使用嵌套调用的替代方法:
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(并尊重您同事的首选代码风格),但您也获得了可读性和可测试性,因为您现在拥有几个更易于理解和单元测试的小方法。