在Java 8中使用先前链接的thenCompose lambdas中的值 [英] Using values from previously chained thenCompose lambdas in Java 8

查看:190
本文介绍了在Java 8中使用先前链接的thenCompose lambdas中的值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

同事喜欢的Java 8编码风格是将异步调用一直链接到整个过程,例如

The Java 8 coding style preferred by my colleagues is chaining asynchronous calls all the way through, e.g.

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

我有类似上面的内容,但是还有一个额外的挑战:我需要在以后的lambda中调用在某些lambda中检索到的值.例如,

I have something like the above, but with an added challenge: I need to recall values retrieved within some of the lambdas, in later lambdas. For example,

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);
    });
}

当我在外部范围内声明Foo foo;Bar bar;时,我收到关于它们不是最终值或实际上不是最终值的错误.而且我读过有关使用包装器使它们有效地定型的信息,但是这样做似乎很不明智(我不明白为什么允许这样做...)

When I declare Foo foo; and Bar bar; in the outside scope, I get errors about them not being final or effectively final. And I read about using a wrapper to make them effectively final, but it seems rather hacky to me to do this (I don't understand why that's allowed...)

我读到Java 8没有添加对元组的支持(尽管它考虑了BiVal,也许我可以用来传递给BiFunction lambdas).所以我尝试使用配对例如

I read that Java 8 didn't add support for tuples (though it considered BiVal, which maybe I could have used to pass to BiFunction lambdas). So I tried using pairs, e.g.

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

,然后在需要重新调用foo的地方

and then wherever I needed to recall foo,

      Foo foo = fooAndB.getKey();

但是,从语义上来说,这是非常错误的.另外,它不起作用!我不知道为什么,因为我认为lambda参数的作用域与它的外部作用域相同,因此所有lambda参数都可以从以后链接的lambda内部访问.

But this feels so semantically wrong. Also, it doesn't work! I don't know why, because I thought a lambda parameter's scope is the same as its outside scope, and so all lambda parameters would be accessible from within later-chained lambdas.

lambda参数的范围实际上是什么?在保持链接的同时,是否有惯用的或至少在语义上没有冒犯性的方式来执行我想做的事情?

基于断链的答案很好,因为它们可能对将来的观看者有用,但就我而言,此回购中与主导风格的偏离可能会导致PR对话和延迟的批准,所以我很乐意保留链接的解决方案.或者,对试图保持链接有多么疯狂的解释或演示.谢谢!

Answers based on breaking the chain are fine as they may be useful for future viewers, but in my case, deviations from the dominant style in this repo may result in drawn out PR conversations and delayed approval, so I'd love a solution that preserves the chaining. Or, an explanation or demonstration as to how insane it would be to try to keep the chaining. Thank you!

推荐答案

由于您提到了同事喜欢的编码风格,因此您可能已经知道使用嵌套调用的替代方法:

Since you mention the coding style preferred by your colleagues, you probably already know the alternative of using nested calls instead:

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);
            });
        });
    });
}

这立即解决了您的问题,但代价是代码的可读性降低了.但是,可以通过从代码中提取一些方法来轻松解决此问题:

This immediately fixes your issue, at the cost of a bit less readable code. But this problem can easily be fixed by extracting some methods from your code:

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));
}

通过这样做,可以避免嵌套的lambda(并尊重同事的首选代码样式),但是由于您现在拥有一些更易于理解和进行单元测试的小方法,因此您还获得了可读性和可测试性.

By doing this, you avoid nested lambdas (and respect the preferred code style of your colleagues), but you also gain in readability and testability since you now have several small methods that are easier to understand and unit test.

这篇关于在Java 8中使用先前链接的thenCompose lambdas中的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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