【问题标题】:Return CompletableFuture<Void> or CompletableFuture<?>?返回 CompletableFuture<Void> 还是 CompletableFuture<?>?
【发布时间】:2016-03-17 22:43:40
【问题描述】:

我想编写一个返回CompletableFuture 的异步方法。未来的唯一目的是跟踪方法何时完成,而不是其结果。返回CompletableFuture&lt;Void&gt;CompletableFuture&lt;?&gt; 会更好吗?是否有理由偏爱其中一个,或者它们可以互换?

请注意,我只询问返回类型,而不是参数列表、变量声明或其他上下文。

【问题讨论】:

    标签: java future return-type completable-future


    【解决方案1】:

    最好使用CompletableFuture&lt;Void&gt;

    Sotirios DelimanolisFuture&lt;?&gt; 发现的According to this answer 是一个小的 API 缺陷。在 Java 6 中,submit() 方法在内部使用了Future&lt;Object&gt;,因此它的返回类型设置为Future&lt;?&gt;。在 Java 7 中,实现更改为在内部使用 Future&lt;Void&gt;,但更改 API 为时已晚,因此返回值保持为 Future&lt;?&gt;

    较新的 Java API 使用 Future&lt;Void&gt;CompletableFuture&lt;Void&gt;。这些是我们应该遵循的例子。

    【讨论】:

      【解决方案2】:

      返回 CompletableFuture 会更好还是 可完成的未来>

      是否有理由偏爱其中一个,或者他们是 可以互换吗?

      代码可能会影响三种上下文:

      • 运行时 - 泛型对它没有影响。
      • 编译 - 我无法想象某些方法会接受 Future&lt;Void&gt; 但不会接受 Future&lt;?&gt; 的情况。
      • 开发 - 如果Future 的结果没有意义,那么最好通过声明向用户说明这一点。

      所以Future&lt;Void&gt; 更可取。

      【讨论】:

        【解决方案3】:

        查看CompletableFuture API,您会发现CompletableFuture&lt;Void&gt; 与无法获得结果(因为它不存在)的副作用类方法一起使用,例如:

        CompletableFuture.runAsync(Runnable runnable);
        

        在这里返回CompletableFuture&lt;Object&gt; 会让人感到困惑,因为实际上没有结果,我们只关心完成。采用ConsumersRunnables 的方法返回CompletableFuture&lt;Void&gt;,例如:thenAcceptthenAcceptAsyncConsumerRunnable 通常用于副作用。

        Void 的另一个用例是当您真的不知道结果时。例如:CompletableFuture.allOf,传递的列表可能是来自 Runnable 的 CompletableFuture,所以我们无法得到结果。

        说了这么多,CompletableFuture&lt;Void&gt;只有在你没有其他选择的情况下才好,如果你能返回结果,那就去吧,如果调用者不感兴趣,他们可能会选择丢弃。您说您只对完成感兴趣,那么是的,CompletableFuture&lt;Void&gt; 会完成这项工作,但是如果您的 API 用户知道CompletableFuture&lt;T&gt; 是一个选项并且您只是代表他们决定他们永远不会需要结果。

        【讨论】:

          【解决方案4】:

          合适的类型取决于它的语义。所有列出的选项都承诺发出完成信号,并可能异步返回异常。

          • CompletableFuture&lt;Void&gt;Void 告诉用户没有预期的结果。
          • CompletableFuture&lt;?&gt; ? 表示包含值的类型在可以传递任何值的意义上是未定义的。

          CompletableFuture 类从CompletionStage 继承了几个便利方法。但它也允许您的方法的调用者触发未来的完成,这似乎是错误的,因为您的方法负责自己发出信号完成。还有一个cancel(...) 方法在CompletableFuture 的默认实现中毫无意义,因为它不会取消执行。

          • Future&lt;Void&gt;Void 告诉用户没有预期的结果。
          • Future&lt;?&gt; ? 表示包含值的类型在可以传递任何值的意义上是未定义的。

          Future 缺少来自CompletionStage 的便捷方法。它不允许触发未来的完成,但可以取消执行。

          下一个选项是CompletionStage&lt;Void&gt;:

          • CompletionStage&lt;Void&gt;Void 告诉用户没有预期的结果。提供了绑定处理程序的便捷方法,但没有 cancel(...) 方法。您的方法的调用者无法触发 CompletionStage 的完成。
          • &lt;CancellableFuture extends Future&lt;Void&gt; &amp; CompletionStage&lt;Void&gt;&gt;:来自Future&lt;Void&gt;CompletionStage&lt;Void&gt; 的方法集。它表明没有结果,存在便利方法以及取消选项。您的方法的调用者无法触发 CompletionStage 的完成。

          cancel(...) 方法的缺失可能适合您的场景,也可能不适合。因此,如果您不需要取消,我建议使用CompletionStage&lt;Void&gt;,如果您需要取消执行的选项,请使用&lt;CancellableFuture extends Future&lt;Void&gt; &amp; CompletionStage&lt;Void&gt;&gt;。如果您选择了&lt;CancellableFuture extends Future&lt;Void&gt; &amp; CompletionStage&lt;Void&gt;&gt;,您可能希望自己创建一个继承自Future&lt;Void&gt;CompletionStage&lt;Void&gt; 的接口用作返回类型,而不是将长类型交集直接放在您的方法声明中。

          您应该避免使用声明的返回类型 CompletableFuture 返回,因为调用者可能会触发未来的完成。这样做会故意导致混乱的代码和令人惊讶的挂起,因为现在还不清楚哪个代码负责触发完成。使用上述更受限制的类型之一,让类型系统防止您的方法调用者意外触发完成。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-06-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-10-28
            • 2023-03-14
            • 2018-06-02
            • 1970-01-01
            相关资源
            最近更新 更多