问题
I want to write an asynchronous method that returns a CompletableFuture. The only purpose of the future is to track when the method is complete, not its result. Would it be better to return CompletableFuture<Void>
or CompletableFuture<?>
? Is there a reason to prefer one or the other, or are they interchangeable?
- CompletableFuture itself returns
CompletableFuture<Void>
from many of its methods. java.nio
has aFuture<Void>
in AsynchronousSocketChannel:Future<Void> connect(SocketAddress remote)
.- On the other hand,
java.util.concurrent
classes like ExecutorService and ScheduledExecutorService returnFuture<?>
: for instance, withFuture<?> submit(Runnable task)
.
Note that I'm only asking about return types, not parameter lists, variable declarations, or other contexts.
回答1:
It is best to use CompletableFuture<Void>
.
According to this answer found by Sotirios Delimanolis, Future<?>
is a minor API flaw. In Java 6 the submit()
method used a Future<Object>
internally, and so its return type was set to Future<?>
. In Java 7 the implementation changed to use Future<Void>
internally, but it was too late to change the API so the return value stayed as Future<?>
.
Newer Java APIs use Future<Void>
and CompletableFuture<Void>
. Those are the examples we should follow.
回答2:
Would it be better to return CompletableFuture<Void> or CompletableFuture<?>
Is there a reason to prefer one or the other, or are they interchangeable?
There are three contexts which code may affect:
- Runtime - generics have no implication on it.
- Compile - I can't imagine a case where some method will accept
Future<Void>
but won't acceptFuture<?>
. - Development - if
Future
's result have no meaning, then it is a good practice to say about that to the users through the declaration.
So Future<Void>
is more preferable.
回答3:
Looking at the CompletableFuture
API you will find that CompletableFuture<Void>
is used with side effects kind of methods where the result can't be obtained (because it doesn't exist), ex:
CompletableFuture.runAsync(Runnable runnable);
returning a CompletableFuture<Object>
here would be confusing because there no result really, we only care about the completion. Methods that take Consumers
and Runnables
return CompletableFuture<Void>
, ex : thenAccept
, thenAcceptAsync
. Consumer
and Runnable
are used for side effects in general.
Another use case for Void
is when you really don't know the result. For example: CompletableFuture.allOf
, the passed list might be a CompletableFuture originated from a Runnable, so we can't get the result.
Having said all of that, CompletableFuture<Void>
is only good if you don't have another option, if you can return the result then please go for it, the caller might choose to discard if they are not interested. You said that you are only interested in the completion, then yes, CompletableFuture<Void>
would do the job, but your API users would hate you if they know that CompletableFuture<T>
was an option and you just decided on their behalf that they will never need the result.
回答4:
The suitable type depends on its semantic. All listed options promise to signal completion and may asynchronously return exceptions.
CompletableFuture<Void>
: TheVoid
tells the user there is no result to be expected.CompletableFuture<?>
The?
means the type of the contains value is undefined in the sense that any value could be delivered.
The CompletableFuture
class inherits several convenience methods from CompletionStage
. But it does also allow the caller of your method to trigger completion of the future which seems wrong because your method is responsible to signal its completion itself. There is also a cancel(...)
method which is pretty pointless in the default implementation of CompletableFuture
as it does not cancel the execution.
Future<Void>
: TheVoid
tells the user there is no result to be expected.Future<?>
The?
means the type of the contains value is undefined in the sense that any value could be delivered.
Future
lacks the convenience methods from CompletionStage
. It does not allow to trigger completion of the future but the execution can be cancelled.
Next option is CompletionStage<Void>
:
CompletionStage<Void>
: TheVoid
tells the user there is no result to be expected. The convenience methods to bind handlers are present but thecancel(...)
method is not. The caller of your method cannot trigger the completion of aCompletionStage
.<CancellableFuture extends Future<Void> & CompletionStage<Void>>
: Set of methods fromFuture<Void>
andCompletionStage<Void>
. It tells that there is no result, convenience methods are present as well as the option to cancel. The caller of your method cannot trigger the completion of aCompletionStage
.
The absence of the cancel(...)
method might fit your scenario or not. Therefore I suggest to go with CompletionStage<Void>
if you don't need cancellation and use <CancellableFuture extends Future<Void> & CompletionStage<Void>>
if you require the option to cancel the execution. If you chose <CancellableFuture extends Future<Void> & CompletionStage<Void>>
you probably want to create an interface yourself that inherits from Future<Void>
and CompletionStage<Void>
to be used as return type instead of putting the long type intersection directly in your method declaration.
You should avoid returning with a declared return type CompletableFuture
because of the possibility for the caller to trigger completion of the future. Doing so deliberately leads to confusing code and surprising hangs because it is not clear which code is responsible to trigger completion anymore. Use one of the mentioned more restricted types to let the type system prevent unintentional completion triggering by the caller of your method.
来源:https://stackoverflow.com/questions/34233375/return-completablefuturevoid-or-completablefuture