【问题标题】:Return CompletableFuture<Void> or CompletableFuture<?>?返回 CompletableFuture<Void> 还是 CompletableFuture<?>?
【发布时间】:2016-03-17 22:43:40
【问题描述】:
我想编写一个返回CompletableFuture 的异步方法。未来的唯一目的是跟踪方法何时完成,而不是其结果。返回CompletableFuture<Void> 或CompletableFuture<?> 会更好吗?是否有理由偏爱其中一个,或者它们可以互换?
请注意,我只询问返回类型,而不是参数列表、变量声明或其他上下文。
【问题讨论】:
标签:
java
future
return-type
completable-future
【解决方案1】:
最好使用CompletableFuture<Void>。
Sotirios Delimanolis、Future<?> 发现的According to this answer 是一个小的 API 缺陷。在 Java 6 中,submit() 方法在内部使用了Future<Object>,因此它的返回类型设置为Future<?>。在 Java 7 中,实现更改为在内部使用 Future<Void>,但更改 API 为时已晚,因此返回值保持为 Future<?>。
较新的 Java API 使用 Future<Void> 和 CompletableFuture<Void>。这些是我们应该遵循的例子。
【解决方案2】:
返回 CompletableFuture 会更好还是
可完成的未来>
是否有理由偏爱其中一个,或者他们是
可以互换吗?
代码可能会影响三种上下文:
- 运行时 - 泛型对它没有影响。
- 编译 - 我无法想象某些方法会接受
Future<Void> 但不会接受 Future<?> 的情况。
- 开发 - 如果
Future 的结果没有意义,那么最好通过声明向用户说明这一点。
所以Future<Void> 更可取。
【解决方案3】:
查看CompletableFuture API,您会发现CompletableFuture<Void> 与无法获得结果(因为它不存在)的副作用类方法一起使用,例如:
CompletableFuture.runAsync(Runnable runnable);
在这里返回CompletableFuture<Object> 会让人感到困惑,因为实际上没有结果,我们只关心完成。采用Consumers 和Runnables 的方法返回CompletableFuture<Void>,例如:thenAccept、thenAcceptAsync。 Consumer 和 Runnable 通常用于副作用。
Void 的另一个用例是当您真的不知道结果时。例如:CompletableFuture.allOf,传递的列表可能是来自 Runnable 的 CompletableFuture,所以我们无法得到结果。
说了这么多,CompletableFuture<Void>只有在你没有其他选择的情况下才好,如果你能返回结果,那就去吧,如果调用者不感兴趣,他们可能会选择丢弃。您说您只对完成感兴趣,那么是的,CompletableFuture<Void> 会完成这项工作,但是如果您的 API 用户知道CompletableFuture<T> 是一个选项并且您只是代表他们决定他们永远不会需要结果。
【解决方案4】:
合适的类型取决于它的语义。所有列出的选项都承诺发出完成信号,并可能异步返回异常。
-
CompletableFuture<Void>:Void 告诉用户没有预期的结果。
-
CompletableFuture<?> ? 表示包含值的类型在可以传递任何值的意义上是未定义的。
CompletableFuture 类从CompletionStage 继承了几个便利方法。但它也允许您的方法的调用者触发未来的完成,这似乎是错误的,因为您的方法负责自己发出信号完成。还有一个cancel(...) 方法在CompletableFuture 的默认实现中毫无意义,因为它不会取消执行。
-
Future<Void>:Void 告诉用户没有预期的结果。
-
Future<?> ? 表示包含值的类型在可以传递任何值的意义上是未定义的。
Future 缺少来自CompletionStage 的便捷方法。它不允许触发未来的完成,但可以取消执行。
下一个选项是CompletionStage<Void>:
-
CompletionStage<Void>:Void 告诉用户没有预期的结果。提供了绑定处理程序的便捷方法,但没有 cancel(...) 方法。您的方法的调用者无法触发 CompletionStage 的完成。
-
<CancellableFuture extends Future<Void> & CompletionStage<Void>>:来自Future<Void> 和CompletionStage<Void> 的方法集。它表明没有结果,存在便利方法以及取消选项。您的方法的调用者无法触发 CompletionStage 的完成。
cancel(...) 方法的缺失可能适合您的场景,也可能不适合。因此,如果您不需要取消,我建议使用CompletionStage<Void>,如果您需要取消执行的选项,请使用<CancellableFuture extends Future<Void> & CompletionStage<Void>>。如果您选择了<CancellableFuture extends Future<Void> & CompletionStage<Void>>,您可能希望自己创建一个继承自Future<Void> 和CompletionStage<Void> 的接口用作返回类型,而不是将长类型交集直接放在您的方法声明中。
您应该避免使用声明的返回类型 CompletableFuture 返回,因为调用者可能会触发未来的完成。这样做会故意导致混乱的代码和令人惊讶的挂起,因为现在还不清楚哪个代码负责触发完成。使用上述更受限制的类型之一,让类型系统防止您的方法调用者意外触发完成。