1前書き

コアJavaは非同期計算のための基本的なAPIを提供します –

Future.


CompletableFuture

はその最新の実装の1つです。

Vavrは

Future

APIに代わる新しい機能を提供します。この記事では、新しいAPIについて説明し、いくつかの新機能を活用する方法を説明します。

Vavrに関するより多くの記事はリンクを見つけることができます:/vavr-tutorial[ここ]。


2 Mavenの依存関係


Future

APIはVavr Mavenの依存関係に含まれています。

それで、あなたのpom.xmlにそれを追加しましょう:

<dependency>
    <groupId>io.vavr</groupId>
    <artifactId>vavr</artifactId>
    <version>0.9.2</version>
</dependency>


3 Vavrの

未来



  • Future

    は、次の2つの状態のいずれかになります。

  • Pending – 計算は進行中です

  • 完了 –

中心的なJavaの

Future

に勝る主な利点は、コールバックを簡単に登録し、操作をブロックしない方法で構成できることです。


4基本的な今後の操作


4.1. 非同期計算の開始

では、Vavrを使って非同期計算を開始する方法を見てみましょう。

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


4.2.

Future


から値を取得する


Future

から値を抽出するには、単に

get()

または

getOrElse()

メソッドを呼び出します。

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


get()



getOrElse()

の違いは、

get()

が最も簡単な解決策であるのに対し、

getOrElse()

は、内部の値を取得できなかった場合に任意の型の値を返すことができるということです。

未来


Future

から値を取得しようとしたときに発生したエラーを処理できるように、

getOrElse()

を使用することをお勧めします。 ** 簡単にするために、次のいくつかの例では

get()

を使用します。

結果を待つ必要がある場合、

get()

メソッドは現在のスレッドをブロックします。

別の方法は、計算が保留されている限り空になる

Option <Try <T>>

を返す、ノンブロッキング

getValue()

メソッドを呼び出すことです。

その後、

Try

オブジェクト内にある計算結果を抽出することができます。

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

値を取得する前に、

Future

に値が含まれているかどうかを確認する必要がある場合があります。

それを単に使うことができます:

resultFuture.isEmpty();

メソッド

isEmpty()

がブロックされていることに注意することが重要です – それはその操作が終了するまでスレッドをブロックします。


4.3. デフォルトの

ExecutorService


を変更する


Futures



ExecutorService

を使って計算を非同期に実行します。デフォルトの

ExecutorService



Executors.newCachedThreadPool()

です。

選択した実装を渡すことで別の

ExecutorService

を使用できます。

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

    assertThat(result)
      .isEqualTo(HELLO);
}


5完了時にアクションを実行する

APIは、

Future

が正常に完了するとすぐにアクションを実行する

onSuccess()

メソッドを提供します。

同様に、メソッド

onFailure()

は、

Future

が失敗したときに実行されます。

簡単な例を見てみましょう。

Future<String> 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<String> resultFuture = Future.of(() -> appendData(initialValue))
  .andThen(finalResult -> System.out.println("Completed - 1: " + finalResult))
  .andThen(finalResult -> System.out.println("Completed - 2: " + finalResult));


6.

Futures


の便利な操作


6.1. 現在のスレッドをブロックする

メソッド

await()

には2つのケースがあります。


  • Future

    が保留中の場合は、現在のスレッドをブロックします。

未来は完成しました
**

Future

が完成すれば、すぐに終了します

この方法を使うのは簡単です:

resultFuture.await();


6.2. 計算をキャンセルする

計算はいつでもキャンセルできます。

resultFuture.cancel();


6.3. 基礎となる

ExecutorService


の取得


Future

によって使用される

ExecutorService

を取得するには、単に

executorService()

を呼び出すだけです。

resultFuture.executorService();


6.4. 失敗した

Future


から

Throwable

を取得する

これを行うには、

io.vavr.control.Option

オブジェクトにラップされた

Throwable

を返す

getCause()

メソッドを使用します。

後で

Option

オブジェクトから

Throwable

を抽出できます。

@Test
public void whenDivideByZero__thenGetThrowable2() {
    Future<Integer> resultFuture = Future.of(() -> 10/0)
      .await();

    assertThat(resultFuture.getCause().get().getMessage())
      .isEqualTo("/by zero");
}

さらに、

failed()メソッドを使用して、インスタンスを

Throwable

インスタンスを保持する

Future__に変換できます。

@Test
public void whenDivideByZero__thenGetThrowable1() {
    Future<Integer> resultFuture = Future.of(() -> 10/0);

    assertThatThrownBy(resultFuture::get)
      .isInstanceOf(ArithmeticException.class);
}


6.5.

isCompleted()、isSuccess()、

、および

isFailure()


これらのメソッドはほとんど自明です。彼らは

Future

が完了したかどうか、それが成功したか失敗したかどうかをチェックします。もちろん、それらのすべてが

boolean

値を返します。

前の例でこれらの方法を使用します。

@Test
public void whenDivideByZero__thenCorrect() {
    Future<Integer> resultFuture = Future.of(() -> 10/0)
      .await();

    assertThat(resultFuture.isCompleted()).isTrue();
    assertThat(resultFuture.isSuccess()).isFalse();
    assertThat(resultFuture.isFailure()).isTrue();
}


6.6. 未来の上に計算を適用する


map()

メソッドを使用すると、保留中の__Futureの上に計算を適用できます。

@Test
public void whenCallMap__thenCorrect() {
    Future<String> futureResult = Future.of(() -> "from Baeldung")
      .map(a -> "Hello " + a)
      .await();

    assertThat(futureResult.get())
      .isEqualTo("Hello from Baeldung");
}


Future

を返す関数を

map()

メソッドに渡すと、入れ子になった

Future

構造体になる可能性があります。これを避けるために、

flatMap()

メソッドを利用することができます。

@Test
public void whenCallFlatMap__thenCorrect() {
    Future<Object> futureMap = Future.of(() -> 1)
      .flatMap((i) -> Future.of(() -> "Hello: " + i));

    assertThat(futureMap.get()).isEqualTo("Hello: 1");
}


6.7. 変換

未来


メソッド

transformValue()

を使用して、

Future

の上に計算を適用し、その内部の値を同じタイプまたは異なるタイプの別の値に変更できます。

@Test
public void whenTransform__thenCorrect() {
    Future<Object> future = Future.of(() -> 5)
      .transformValue(result -> Try.of(() -> HELLO + result.get()));

    assertThat(future.get()).isEqualTo(HELLO + 5);
}


6.8. ジッパー

未来


APIは

Futures

をタプルにまとめて圧縮する

zip()

メソッドを提供します – タプルは互いに関連しているかどうかわからないいくつかの要素の集まりです。それらは異なる種類のものでもあり得る。簡単な例を見てみましょう。

@Test
public void whenCallZip__thenCorrect() {
    Future<String> f1 = Future.of(() -> "hello1");
    Future<String> f2 = Future.of(() -> "hello2");

    assertThat(f1.zip(f2).get())
      .isEqualTo(Tuple.of("hello1", "hello2"));
}

ここで注意しなければならないのは、結果の

Future

は、少なくとも1つの基本

Futures

がまだ保留中である限り保留中であるということです。


6.9.

Futures



CompletableFutures


の間の変換

APIは、

java.util.CompletableFuture

との統合をサポートしています。そのため、コアのJava APIのみがサポートする操作を実行する場合は、

Future



CompletableFuture

に簡単に変換できます。

それができる方法を見てみましょう。

@Test
public void whenConvertToCompletableFuture__thenCorrect()
  throws Exception {

    CompletableFuture<String> convertedFuture = Future.of(() -> HELLO)
      .toCompletableFuture();

    assertThat(convertedFuture.get())
      .isEqualTo(HELLO);
}


fromCompletableFuture()

メソッドを使って

CompletableFuture



Future

に変換することもできます。


6.10. 例外処理


Future

が失敗した場合、いくつかの方法でエラーを処理できます。

たとえば、

recover()

メソッドを使用して、エラーメッセージなどの別の結果を返すことができます。

@Test
public void whenFutureFails__thenGetErrorMessage() {
    Future<String> future = Future.of(() -> "Hello".substring(-1))
      .recover(x -> "fallback value");

    assertThat(future.get())
      .isEqualTo("fallback value");
}

あるいは、

recoverWith()

を使用して別の

Future

計算の結果を返すことができます。

@Test
public void whenFutureFails__thenGetAnotherFuture() {
    Future<String> future = Future.of(() -> "Hello".substring(-1))
      .recoverWith(x -> Future.of(() -> "fallback value"));

    assertThat(future.get())
      .isEqualTo("fallback value");
}

メソッド

fallbackTo()

は、エラーを処理するもう1つの方法です。それは

Future

で呼び出され、パラメータとして別の

Future

を受け入れます。

最初の

Future

が成功した場合は、その結果を返します。

そうでなければ、2番目の

Future

が成功すると、その結果を返します。両方の

Futures

が失敗した場合、

failed()

メソッドは最初の

Future

のエラーを保持する

Throwable



Future

を返します。

@Test
public void whenBothFuturesFail__thenGetErrorMessage() {
    Future<String> f1 = Future.of(() -> "Hello".substring(-1));
    Future<String> f2 = Future.of(() -> "Hello".substring(-2));

    Future<String> errorMessageFuture = f1.fallbackTo(f2);
    Future<Throwable> errorMessage = errorMessageFuture.failed();

    assertThat(
      errorMessage.get().getMessage())
      .isEqualTo("String index out of range: -1");
}


7. 結論

この記事では、「未来」とは何かを見て、その重要な概念をいくつか学びました。いくつかの実用的な例を使用して、APIの機能のいくつかについても説明しました。

コードのフルバージョンはhttps://github.com/eugenp/tutorials/tree/master/vavr[GitHubで利用可能]です。