Подтвердить что ты не робот

Возврат CompletedFuture <Void> или CompletableFuture <?>?

Я хочу написать асинхронный метод, который возвращает CompletableFuture. Единственная цель будущего - отслеживать, когда метод завершен, а не его результат. Было бы лучше вернуться CompletableFuture<Void> или CompletableFuture<?>? Есть ли причина предпочесть тот или другой, или они взаимозаменяемы?

  • CompletableFuture возвращает CompletableFuture<Void> из многих своих методов.
  • java.nio имеет Future<Void> в AsynchronousSocketChannel: Future<Void> connect(SocketAddress remote).
  • С другой стороны, java.util.concurrent классы, такие как ExecutorService и ScheduledExecutorService return Future<?>: например, с Future<?> submit(Runnable task).

Обратите внимание, что я только спрашиваю о типах возврата, а не о списках параметров, объявлениях переменных или о других контекстах.

4b9b3361

Ответ 1

Лучше всего использовать CompletableFuture<Void>.

В соответствии с этим ответом, найденным Sotirios Delimanolis, Future<?> является незначительным недостатком API. В Java 6 метод submit() использовал Future<Object> внутренне, поэтому его возвращаемый тип был установлен на Future<?>. В Java 7 реализация изменилась на использование Future<Void> внутренне, но было слишком поздно менять API, чтобы возвращаемое значение оставалось как Future<?>.

В новых API Java используются Future<Void> и CompletableFuture<Void>. Вот примеры, которым мы должны следовать.

Ответ 2

Было бы лучше возвратить CompletableFuture <Void> или CompletableFuture <? >

Есть ли причина предпочесть тот или другой, или они взаимозаменяемы?

Существует три контекста, которые могут повлиять на код:

  • Runtime - дженерики не имеют никакого отношения к этому.
  • Скомпилировать - я не могу представить случай, когда какой-то метод примет Future<Void>, но не примет Future<?>.
  • Разработка - если результат Future не имеет смысла, то это хорошая практика, чтобы сказать об этом пользователям через декларацию.

Так что Future<Void> является более предпочтительным.

Ответ 3

Глядя на API CompletableFuture, вы обнаружите, что CompletableFuture<Void> используется с методами побочных эффектов, где результат не может быть получен (потому что его не существует), например:

CompletableFuture.runAsync(Runnable runnable);

возврат CompletableFuture<Object> здесь будет запутанным, потому что результата нет, мы только заботимся о завершении. Методы, принимающие Consumers и Runnables return CompletableFuture<Void>, ex: thenAccept, thenAcceptAsync. Consumer и Runnable используются для побочных эффектов в целом.

Другим вариантом использования Void является то, что вы действительно не знаете результат. Например: CompletableFuture.allOf, переданный список может быть CompletableFuture, созданный из Runnable, поэтому мы не можем получить результат.

Сказав все это, CompletableFuture<Void> хорош только в том случае, если у вас нет другого варианта, если вы можете вернуть результат, пожалуйста, подойдите к нему, вызывающий может выбрать отказаться, если они не заинтересованы. Вы сказали, что вас интересует только завершение, тогда да, CompletableFuture<Void> выполнит эту работу, но ваши пользователи API будут вас ненавидеть, если они знают, что CompletableFuture<T> был вариантом, и вы просто решили от их имени, что они никогда не будут нужен результат.

Ответ 4

Подходящий тип зависит от его семантики. Все перечисленные опции обещают завершение сигнала и могут асинхронно возвращать исключения.

  • CompletableFuture<Void>: Void сообщает пользователю, что результата не ожидается.
  • CompletableFuture<?> Значение ? означает, что тип значения содержит undefined в том смысле, что любое значение может быть доставлено.

Класс CompletableFuture наследует несколько удобных методов из CompletionStage. Но это также позволяет вызывающему вашему методу инициировать завершение будущего, которое кажется неправильным, потому что ваш метод отвечает за сигнализацию о его завершении. Существует также метод cancel(...), который довольно бессмыслен в реализации по умолчанию CompletableFuture по умолчанию, поскольку он не отменяет выполнение.

  • Future<Void>: Void сообщает пользователю, что результата не ожидается.
  • Future<?> ? означает, что тип значения содержит undefined в том смысле, что любое значение может быть доставлено.

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 из-за возможности для вызывающего пользователя инициировать завершение будущего. Выполнение этого преднамеренно приводит к запутыванию кода и неожиданным зависаниям, потому что неясно, какой код отвечает за запуск завершения. Используйте один из упомянутых более ограниченных типов, чтобы система типов предотвращала непреднамеренное завершение запуска вызывающим устройством вашего метода.