Ответ 1
Короткий ответ
Нет, более естественно использовать Callable
вместо Supplier
в CompletableFuture.supplyAsync
. Аргумент почти полностью связан с семантикой, так что это нормально, если вы все еще чувствуете себя неубежденным впоследствии.
Длинный ответ
Функциональные интерфейсы Callable
и Supplier
функциональных интерфейсов /SAM практически эквивалентны в функции (pardon pun), но их происхождение и предполагаемое использование отличаются.
Callable
был создан как часть пакета java.util.concurrent
. Этот пакет появился перед огромными изменениями вокруг лямбда-выражений в Java 8 и первоначально был сосредоточен на ряде инструментов, которые помогли вам написать параллельный код, не сильно отклоняясь от классической модели многопоточности.
Основной целью Callable
было абстрактное действие, которое может быть выполнено в другом потоке и которое возвращает результат. От Callable
Javadoc:
Интерфейс
Callable
похож наRunnable
, поскольку оба предназначен для классов, экземпляры которых потенциально выполняются посредством другой поток.
Supplier
был создан как часть пакета java.util.function
. Этот пакет стал неотъемлемой частью вышеупомянутых изменений в Java 8. Он предоставляет общие функциональные типы, которые могут быть нацелены на лямбда-выражения и ссылки на методы.
Один из таких типов - это функция без параметров, которая возвращает результат (т.е. функцию, которая предоставляет функцию типа или Supplier
).
Итак, почему Supplier
, а не Callable
?
CompletableFuture
является частью дополнений к пакету java.util.concurrent
, которые были вдохновлены вышеупомянутыми изменениями в Java 8 и позволяют разработчику создавать его код функциональным, неявно параллелизуемым образом, вместо явной обработки concurrency внутри него.
Его метод supplyAsync
нуждается в способе предоставления результата определенного типа и его большей заинтересованности в этом результате, а не в действии, предпринятом для достижения этого результата. Это также не обязательно касается исключительного завершения (см. Также раздел Как насчет...).
Тем не менее, если Runnable
используется для не-параметров, функционального интерфейса без результата, не следует использовать Callable
для no-parameters, функционального интерфейса с одним результатом?
Не обязательно.
Абстракция для функции, которая не имеет параметра и не возвращает результат (и поэтому полностью работает через побочные эффекты во внешнем контексте), не была включена в java.util.function
. Это означает, что (несколько раздражающе) Runnable
используется везде, где необходим такой функциональный интерфейс.
Как насчет отмеченного Exception
, который может быть выбрано Callable.call()
?
Это небольшой признак предполагаемой семантической разницы между Callable
и Supplier
.
A Callable
- это действие, которое может быть выполнено в другом потоке, и которое позволяет вам проверять его побочные эффекты в результате его выполнения. Если все идет хорошо, вы получаете результат определенного типа, но поскольку при выполнении некоторых действий (особенно в многопоточном контексте) могут возникать исключительные ситуации, вы также можете определить и обработать такие исключительные ситуации.
A Supplier
С другой стороны, это функция, на которую вы полагаетесь на поставку объектов определенного типа. Исключительные ситуации не обязательно должны стать вашей ответственностью в качестве прямого потребителя Supplier
. Это происходит потому, что:
- ... функциональные интерфейсы часто используются для определения определенного этапа в многоэтапном процессе для создания или изменения данных, а обработка
Exception
может быть отдельной стадией, если вы заботитесь - ... явная обработка
Exception
значительно снижает выразительные возможности функциональных интерфейсов, лямбда-выражений и ссылок на методы.