Java 8.の同時実行機能のいくつかを調査し始めたところです。少し混乱したのは、これらの2つの静的メソッドです。
CompletableFuture<Void> runAsync(Runnable runnable)
CompletableFuture<U> supplyAsync(Supplier<U> supplier)
インターフェースサプライヤーを使用する理由を誰か知っていますか?値を返すRunnableのアナロジーであるCallableを使用するほうが自然ではありませんか?それは、サプライヤーが処理できない例外をスローしないためですか?
短い答え
いいえ、_CompletableFuture.supplyAsync
_でCallable
の代わりにSupplier
を使用するのは自然なことではありません。議論はほぼ完全にセマンティクスに関するものなので、後でまだ確信が持てない場合でも問題ありません。
長い答え
Callable
およびSupplier
機能インターフェース/ SAMタイプは、機能的には実質的に同等です(しゃれを許します)が、その起源と使用目的は異なります。
Callable
は、_Java.util.concurrent
_パッケージの一部として作成されました。このパッケージは、Java 8のラムダ式に関する大幅な変更の前に提供されたものであり、当初は、従来のハンズオンマルチスレッドモデルから大きく逸脱することなく、並行コードの記述に役立つさまざまなツールに集中していました。
Callable
の主な目的は、別のスレッドで実行でき、結果を返すアクションを抽象化することでした。 Callable
のJavadocから:
Callable
インターフェースはRunnable
に似ていますが、どちらもインスタンスが別のスレッドによって実行される可能性があるクラス用に設計されています。
Supplier
は、_Java.util.function
_パッケージの一部として作成されました。このパッケージは、前述のJava 8.での変更の不可欠な部分として提供されました。このパッケージは、ラムダ式およびメソッド参照のターゲットとなる一般的な関数型を提供します。
そのようなタイプの1つは、結果を返すパラメーターのない関数です(つまり、あるタイプを提供する関数またはSupplier
関数)。
それで、なぜSupplier
ではなくCallable
なのか?
CompletableFuture
は、前述のJava 8の変更)に触発された_Java.util.concurrent
_パッケージへの追加の一部であり、開発者が関数でコードを構築できるようにします。内部で並行性を明示的に処理する代わりに、暗黙的に並列化可能な方法。
そのsupplyAsync
メソッドには、特定のタイプの結果を提供する方法が必要であり、この結果に関心があり、この結果に到達するために実行されるアクションではありません。また、必ずしも例外的な完了を気にする必要はありません(以下のWhat about the ...の段落も参照)。
それでも、Runnable
をパラメーターなしの結果なしの機能的インターフェースに使用する場合、Callable
をパラメーターなしの単一結果の機能的インターフェースに使用しないでください? =
必ずしも。
パラメータがなく、結果を返さない(したがって、完全に外部コンテキストの副作用によって機能する)関数の抽象化は、_Java.util.function
_に含まれていませんでした。これは、そのような機能的なインターフェイスが必要な場合は常に(やや厄介なことに)Runnable
が使用されることを意味します。
スローできるチェック済みException
については_Callable.call()
?
これは、Callable
とSupplier
の意図された意味上の違いの小さな兆候です。
Callable
は、別のスレッドで実行できるアクションであり、実行の結果としてその副作用を検査できます。すべてがうまくいくと、特定のタイプの結果が得られますが、一部のアクション(特にマルチスレッドのコンテキスト)を実行すると例外的な状況が発生する可能性があるため、そのような例外的な状況を定義して処理することもできます。
一方、Supplier
は、何らかのタイプのオブジェクトを提供するために使用する関数です。例外的な状況は、Supplier
の直接の消費者としてのあなたの責任である必要はありません。これはそうです:
Exception
sの処理は、必要に応じて別のステージにすることができますException
sを明示的に処理すると、関数型インターフェース、ラムダ式、メソッド参照の表現力が大幅に低下します。