runnable )を実装するクラスで run() で呼び出しているメソッドは、例外をスローするように設計されています。
しかし、Javaコンパイラーはそれを許可せず、try/catchで囲むことを提案します。
問題は、それをtry/catchで囲むことでその特定のrun() 役に立たないことです。 Idoその例外をスローしたい。
run() 自体にthrows
を指定すると、コンパイラはException is not compatible with throws clause in Runnable.run()
と文句を言います。
通常、私は run() に例外をスローさせなくても大丈夫です。しかし、私はその機能を持たなければならないユニークな状況にあります。
この制限を回避するにはどうすればよいですか?
Runnable
を実装するクラスをThread
フレームワークに渡したい場合は、そのフレームワークのルールに従って操作する必要があります。それ以外の場合は悪いアイデアである理由を説明するErnest Friedman-Hillの回答を参照してください。
ただし、コード内でrun
メソッドを直接呼び出して、呼び出し元のコードが例外を処理できるようにしたいという思いがあります。
この問題への答えは簡単です。スレッドライブラリのRunnable
インターフェイスを使用しないでください。代わりに、チェックされた例外をスローできるように変更されたシグネチャを持つ独自のインターフェイスを作成してください。
public interface MyRunnable
{
void myRun ( ) throws MyException;
}
スレッドフレームワークでの使用に適した実際のRunnable
(チェック済み例外を処理することにより)にこのインターフェイスを変換するアダプターを作成することもできます。
代わりにCallable
を使用して、ExecutorService
に送信し、FutureTask.isDone()
によって返されるExecutorService.submit()
で結果を待つことができます。
isDone()
がtrueを返すとき、FutureTask.get()
を呼び出します。 Callable
がException
をスローした場合、FutureTask.get()
はException
もスローし、元の例外はException.getCause()
。
run()
がチェック例外をスローした場合、何がキャッチされますか? run()
呼び出しをハンドラーで囲む方法はありません。それを呼び出すコードを記述しないためです。
run()
メソッドでチェック済みの例外をキャッチし、代わりに未変更の例外(つまり、RuntimeException
)をスローできます。これにより、スタックトレースでスレッドが終了します。おそらくそれがあなたの望みです。
代わりにrun()
メソッドでエラーをどこかに報告したい場合は、run()
メソッドのcatch
ブロックが呼び出すコールバックメソッドを提供するだけです。そのメソッドは例外オブジェクトをどこかに保存し、関心のあるスレッドがその場所でオブジェクトを見つけることができます。
はい、run()
メソッドからchecked例外をスローする方法がありますが、とてもひどいので共有しません。
代わりにできることは次のとおりです。ランタイム例外が実行するのと同じメカニズムを使用します。
_@Override
public void run() {
try {
/* Do your thing. */
...
} catch (Exception ex) {
Thread t = Thread.currentThread();
t.getUncaughtExceptionHandler().uncaughtException(t, ex);
}
}
_
他の人が指摘したように、run()
メソッドが実際にThread
のターゲットである場合、例外はスローされないため、例外をスローしても意味がありません。例外をスローすると、例外をスローしないのと同じ効果があります(なし)。
Thread
ターゲットでない場合は、Runnable
を使用しないでください。たとえば、おそらく Callable
の方が適しています。
@FunctionalInterface
public interface CheckedRunnable<E extends Exception> extends Runnable {
@Override
default void run() throws RuntimeException {
try {
runThrows();
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
void runThrows() throws E;
}
一部の人々は、ルールに従ってプレーしなければならないことをあなたに納得させようとします。聞いてください。しかし、従うかどうかは、状況に応じて自分で決める必要があります。現実は、「ルールに従ってプレイする必要があります」ではなく「ルールに従ってプレイする必要があります」です。ルールを守らないと、結果が生じる可能性があることに注意してください。
この状況はRunnable
の状況に適用されるだけでなく、Java 8を使用して、ストリームや、機能インターフェースが導入されていない他の場所のコンテキストでも非常に頻繁にたとえば、Consumer
、Supplier
、Function
、BiFunction
などはすべて、チェック例外を処理する機能なしで宣言されています。
それでは、状況とオプションは何ですか?以下のテキストでは、Runnable
は、例外を宣言しない、または手元のユースケースに対して限定されすぎる例外を宣言するすべての機能インターフェイスを表しています。
Runnable
を宣言したので、Runnable
を別のものに置き換えることができます。Runnable
を_Callable<Void>
_に置き換えることを検討してください。基本的に同じことですが、例外をスローできます。最後に_return null
_する必要がありますが、これはやや面倒です。Runnable
を、必要な例外を正確にスローできる独自のカスタム_@FunctionalInterface
_に置き換えることを検討してください。Runnable
の代わりに_Callable<Void>
_を使用できます。RuntimeException
で例外をラップできます。以下を試すことができます。ちょっとしたハックですが、時にはハックが必要な場合もあります。なぜなら、例外をチェックするかどうかは、そのタイプによって定義されますが、実際には状況によって実際に定義される必要があるためです。
_@FunctionalInterface
public interface ThrowingRunnable extends Runnable {
@Override
default void run() {
try {
tryRun();
} catch (final Throwable t) {
throwUnchecekd(t);
}
}
private static <E extends RuntimeException> void throwUnchecked(Throwable t) {
throw (E) t;
}
void tryRun() throws Throwable;
}
_
new RuntimeException(t)
よりもスタックトレースが短いので、こちらが好きです。
次のことができます。
_executorService.submit((ThrowingRunnable) () -> {throw new Exception()});
_
免責事項:この方法で未チェックのキャストを実行する機能は、ジェネリック型情報がコンパイル時だけでなく実行時にも処理されるときに、将来のバージョンのJavaで実際に削除される可能性があります。