Рубрики
Без рубрики

Введение в функцию в Var

Узнайте о будущем API Фавра как альтернативе будущему API Java.

Автор оригинала: baeldung.

1. введение

Ядро Java предоставляет базовый API для асинхронных вычислений – Будущее. CompletableFuture |/является одной из его новейших реализаций.

Vavr предоставляет свою новую функциональную альтернативу API Future . В этой статье мы обсудим новый API и покажем, как использовать некоторые из его новых функций.

Дополнительные статьи о Var можно найти здесь .

2. Зависимость Maven

API Future включен в зависимость Var Maven.

Итак, давайте добавим его в ваш pom.xml :


    io.vavr
    vavr
    0.9.2

Мы можем найти последнюю версию зависимости от Maven Central .

3. Будущее Вавра

То Будущее может находиться в одном из двух состояний:

  • В ожидании – вычисление продолжается
  • Завершено – вычисление завершилось успешно с результатом, не удалось с исключением или было отменено

Основное преимущество перед ядром Java Будущее заключается в том, что мы можем легко регистрировать обратные вызовы и составлять операции неблокирующим способом.

4. Основные Будущие Операции

4.1. Запуск Асинхронных Вычислений

Теперь давайте посмотрим, как мы можем начать асинхронные вычисления с помощью Var:

String initialValue = "Welcome to ";
Future resultFuture = Future.of(() -> someComputation());

4.2. Извлечение значений из будущего

Мы можем извлечь значения из Future , просто вызвав один из методов get() или getOrElse() :

String result = resultFuture.getOrElse("Failed to get underlying value.");

Разница между get() и getOrElse() заключается в том, что get() является самым простым решением, в то время как getOrElse() позволяет нам возвращать значение любого типа в случае, если мы не смогли получить значение внутри Будущего .

Рекомендуется использовать getOrElse () , чтобы мы могли обрабатывать любые ошибки, возникающие при попытке получить значение из Future . Для простоты мы просто будем использовать get() в следующих нескольких примерах.

Обратите внимание, что метод get() блокирует текущий поток, если необходимо дождаться результата.

Другой подход заключается в вызове неблокирующего метода GetValue () , который возвращает параметр > , который будет пустым, пока ожидаются вычисления.

Затем мы можем извлечь результат вычисления, который находится внутри объекта Try :

Option> futureOption = resultFuture.getValue();
Try futureTry = futureOption.get();
String result = futureTry.get();

Иногда нам нужно проверить, содержит ли Future значение, прежде чем извлекать из него значения.

Мы можем просто сделать это с помощью:

resultFuture.isEmpty();

Важно отметить, что метод isEmpty() блокирует – он будет блокировать поток до тех пор, пока его работа не будет завершена.

4.3. Изменение службы ExecutorService по умолчанию

Фьючерсы используют ExecutorService для асинхронного выполнения своих вычислений. По умолчанию ExecutorService является Executors.newCachedThreadPool() .

Мы можем использовать другой ExecutorService , передав реализацию по нашему выбору:

@Test
public void whenChangeExecutorService_thenCorrect() {
    String result = Future.of(newSingleThreadExecutor(), () -> HELLO)
      .getOrElse(error);
    
    assertThat(result)
      .isEqualTo(HELLO);
}

5. Выполнение Действий По Завершении

API предоставляет метод onSuccess () , который выполняет действие, как только Future завершается успешно.

Аналогично, метод on Failure() выполняется при сбое Будущего .

Давайте рассмотрим краткий пример:

Future resultFuture = Future.of(() -> appendData(initialValue))
  .onSuccess(v -> System.out.println("Successfully Completed - Result: " + v))
  .onFailure(v -> System.out.println("Failed - Result: " + v));

Метод onComplete() принимает действие, которое будет выполнено, как только Future завершит свое выполнение, независимо от того, было ли Future успешным. Метод andThen() похож на onComplete() – он просто гарантирует, что обратные вызовы выполняются в определенном порядке:

Future resultFuture = Future.of(() -> appendData(initialValue))
  .andThen(finalResult -> System.out.println("Completed - 1: " + finalResult))
  .andThen(finalResult -> System.out.println("Completed - 2: " + finalResult));

6. Полезные операции с Фьючерсами

6.1. Блокировка текущего потока

Метод await() имеет два случая:

  • если Future находится в ожидании, он блокирует текущий поток до тех пор, пока будущее не завершится
  • если Future завершен, он завершается немедленно

Использование этого метода очень просто:

resultFuture.await();

6.2. Отмена вычисления

Мы всегда можем отменить вычисление:

resultFuture.cancel();

6.3. Получение базового сервиса ExecutorService

Чтобы получить ExecutorService , который используется Future , мы можем просто вызвать ExecutorService() :

resultFuture.executorService();

6.4. Получение Броска из Неудачного будущего

Мы можем сделать это с помощью метода getCause () , который возвращает Throwable , завернутый в io.var.control.Опция объект.

Позже мы можем извлечь Throwable из объекта Option :

@Test
public void whenDivideByZero_thenGetThrowable2() {
    Future resultFuture = Future.of(() -> 10 / 0)
      .await();
    
    assertThat(resultFuture.getCause().get().getMessage())
      .isEqualTo("/ by zero");
}

Кроме того, мы можем преобразовать наш экземпляр в Future holding a Выбрасываемый экземпляр с использованием метода failed() :

@Test
public void whenDivideByZero_thenGetThrowable1() {
    Future resultFuture = Future.of(() -> 10 / 0);
    
    assertThatThrownBy(resultFuture::get)
      .isInstanceOf(ArithmeticException.class);
}

6.5. завершено(), является успехом () и является неудачей()

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

Мы собираемся использовать эти методы в предыдущем примере:

@Test
public void whenDivideByZero_thenCorrect() {
    Future resultFuture = Future.of(() -> 10 / 0)
      .await();
    
    assertThat(resultFuture.isCompleted()).isTrue();
    assertThat(resultFuture.isSuccess()).isFalse();
    assertThat(resultFuture.isFailure()).isTrue();
}

6.6. Применение вычислений поверх будущего

Метод map() позволяет нам применять вычисления поверх ожидающего Будущего:

@Test
public void whenCallMap_thenCorrect() {
    Future futureResult = Future.of(() -> "from Baeldung")
      .map(a -> "Hello " + a)
      .await();
    
    assertThat(futureResult.get())
      .isEqualTo("Hello from Baeldung");
}

Если мы передадим функцию, которая возвращает Future методу map () , мы можем получить вложенную структуру Feature . Чтобы избежать этого, мы можем использовать метод flatMap() :

@Test
public void whenCallFlatMap_thenCorrect() {
    Future futureMap = Future.of(() -> 1)
      .flatMap((i) -> Future.of(() -> "Hello: " + i));
         
    assertThat(futureMap.get()).isEqualTo("Hello: 1");
}

6.7. Трансформация фьючерсов

Метод transform Value() может использоваться для применения вычисления поверх Будущего и изменения значения внутри него на другое значение того же типа или другого типа:

@Test
public void whenTransform_thenCorrect() {
    Future future = Future.of(() -> 5)
      .transformValue(result -> Try.of(() -> HELLO + result.get()));
                
    assertThat(future.get()).isEqualTo(HELLO + 5);
}

6.8. Сжатие фьючерсов

API предоставляет метод zip () , который объединяет Фьючерсы в кортежи – кортеж представляет собой набор из нескольких элементов, которые могут быть связаны или не связаны друг с другом. Они также могут быть разных типов. Давайте рассмотрим краткий пример:

@Test
public void whenCallZip_thenCorrect() {
    Future f1 = Future.of(() -> "hello1");
    Future f2 = Future.of(() -> "hello2");
    
    assertThat(f1.zip(f2).get())
      .isEqualTo(Tuple.of("hello1", "hello2"));
}

Здесь следует отметить, что результирующее Будущее будет ожидаться до тех пор, пока по крайней мере одна из базовых Функций все еще находится в ожидании.

6.9. Конвертация между Фьючерсами и завершенными фьючерсами

API поддерживает интеграцию с java.util.CompletableFuture . Таким образом, мы можем легко преобразовать Future в CompletableFuture , если мы хотим выполнять операции, которые поддерживает только основной Java API.

Давайте посмотрим, как мы можем это сделать:

@Test
public void whenConvertToCompletableFuture_thenCorrect()
  throws Exception {
 
    CompletableFuture convertedFuture = Future.of(() -> HELLO)
      .toCompletableFuture();
    
    assertThat(convertedFuture.get())
      .isEqualTo(HELLO);
}

Мы также можем преобразовать CompletableFuture в Future с помощью метода from CompletableFuture () .

6.10. Обработка исключений

После сбоя Future мы можем справиться с ошибкой несколькими способами.

Например, мы можем использовать метод recover() для возврата другого результата, например сообщения об ошибке:

@Test
public void whenFutureFails_thenGetErrorMessage() {
    Future future = Future.of(() -> "Hello".substring(-1))
      .recover(x -> "fallback value");
    
    assertThat(future.get())
      .isEqualTo("fallback value");
}

Или мы можем вернуть результат другого функции вычисления с помощью recover With() :

@Test
public void whenFutureFails_thenGetAnotherFuture() {
    Future future = Future.of(() -> "Hello".substring(-1))
      .recoverWith(x -> Future.of(() -> "fallback value"));
    
    assertThat(future.get())
      .isEqualTo("fallback value");
}

Метод fall back To () – это еще один способ обработки ошибок. Он вызывается на Future и принимает другой Future в качестве параметра.

Если первый Future успешен, то он возвращает свой результат. В противном случае, если второй Future будет успешным, он вернет свой результат. Если обе функции /не работают, то метод failed() возвращает Future a Throwable , который содержит ошибку первого Feature :

@Test
public void whenBothFuturesFail_thenGetErrorMessage() {
    Future f1 = Future.of(() -> "Hello".substring(-1));
    Future f2 = Future.of(() -> "Hello".substring(-2));
    
    Future errorMessageFuture = f1.fallbackTo(f2);
    Future errorMessage = errorMessageFuture.failed();
    
    assertThat(
      errorMessage.get().getMessage())
      .isEqualTo("String index out of range: -1");
}

7. Заключение

В этой статье мы увидели, что такое Будущее , и узнали некоторые из его важных концепций. Мы также рассмотрели некоторые функции API, используя несколько практических примеров.

Полная версия кода доступна на GitHub .