返回 CompletableFuture<Void> 还是 CompletableFuture<?>?
Posted
技术标签:
【中文标题】返回 CompletableFuture<Void> 还是 CompletableFuture<?>?【英文标题】:Return CompletableFuture<Void> or CompletableFuture<?>? 【发布时间】:2016-03-17 22:43:40 【问题描述】:我想编写一个返回CompletableFuture
的异步方法。未来的唯一目的是跟踪方法何时完成,而不是其结果。返回CompletableFuture<Void>
或CompletableFuture<?>
会更好吗?是否有理由偏爱其中一个,或者它们可以互换?
CompletableFuture
本身从它的许多方法中返回 CompletableFuture<Void>
。
java.nio
在AsynchronousSocketChannel
中有一个Future<Void>
:Future<Void> connect(SocketAddress remote)
。
另一方面,java.util.concurrent
类如 ExecutorService
和 ScheduledExecutorService
返回 Future<?>
:例如,Future<?> submit(Runnable task)
。
请注意,我只询问返回类型,而不是参数列表、变量声明或其他上下文。
【问题讨论】:
【参考方案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
返回,因为调用者可能会触发未来的完成。这样做会故意导致混乱的代码和令人惊讶的挂起,因为现在还不清楚哪个代码负责触发完成。使用上述更受限制的类型之一,让类型系统防止您的方法调用者意外触发完成。
【讨论】:
以上是关于返回 CompletableFuture<Void> 还是 CompletableFuture<?>?的主要内容,如果未能解决你的问题,请参考以下文章
CompletableFutureCompletableFuture测试runAsync()方法调用CompletableFuture.join()/get()方法阻塞主线程