Завершенное предложение

Я только начал изучать некоторые функции concurrency Java 8. Одна вещь немного смутила меня этими двумя статическими методами:

CompletableFuture<Void> runAsync(Runnable runnable) 
CompletableFuture<U> supplyAsync(Supplier<U> supplier)

Кто-нибудь знает, почему они решили использовать интерфейс поставщика? Не более ли естественно использовать Callable, что является аналогией Runnable, которая возвращает значение? Это потому, что поставщик не выбрасывает исключение, которое невозможно обработать?

Ответы

Ответ 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 значительно снижает выразительные возможности функциональных интерфейсов, лямбда-выражений и ссылок на методы.