シグニチャの例外キャッチと Java ジェネリックに関連する予期しない問題に遭遇しました。問題のコードは次のとおりです (説明は次のとおりです)。
public class StackOverflowTest {
private static class WrapperBuilder {
public static <T> ResultWrapper of(final T result) {
return new ResultWrapper<>(result);
}
public static ResultWrapper of(final RuntimeException exc) {
return new ResultWrapper<>(exc);
}
}
private static class ResultWrapper<T> {
private final T result;
private final RuntimeException exc;
ResultWrapper(final T result) {
this.result = result;
this.exc = null;
}
ResultWrapper(final RuntimeException exc) {
this.result = null;
this.exc = exc;
}
public Boolean hasException() {
return this.exc != null;
}
public T get() {
if (hasException()) {
throw exc;
}
return result;
}
}
private static class WrapperTransformer {
public ResultWrapper<Result> getResult(ResultWrapper originalWrappedResult) {
if (originalWrappedResult.hasException()) {
try {
originalWrappedResult.get();
} catch (Exception e) {
return WrapperBuilder.of(e);
}
}
return originalWrappedResult; // Transformation is a no-op, here
}
}
private static class Result {}
WrapperTransformer wrapper = new WrapperTransformer();
@Test
public void testBehaviour() {
ResultWrapper wrappedResult = WrapperBuilder.of(new RuntimeException());
final ResultWrapper<Result> result = wrapper.getResult(wrappedResult);
assertTrue(result.hasException()); // fails!
}
}
今のところ、スタイルの悪い質問はさておき (ここで行っていることを実行するためのより良い方法があることを完全に認めています!)、これは次のビジネス ロジックの簡略化され匿名化されたバージョンです。
- クラス
ResultWrapper
は、ダウンストリーム サービスへの呼び出しの結果をラップします。呼び出しの結果、または結果の例外のいずれかが含まれます - クラス
WrapperTransformer
は、何らかの方法で ResultWrapper を変換する責任があります (ただし、ここでは、「変換」はノーオペレーションです)。
上記のテストは失敗します。デバッグの結果、これは がWrapperBuilder.of(e)
実際にはジェネリック メソッド (つまりof(final T result)
) を呼び出しているためであると判断しました。ジェネリック引数が「貪欲」である場合、それは(一種の)意味があります- aRuntimeException
はaT
であるため、そのメソッドは賢明な(意図しないが)選択です。
ただし、DownstreamWrapper::getResult
メソッドが次のように変更された場合:
// i.e. explicitly catch RuntimeException, not Exception
} catch (RuntimeException e) {
return WrapperBuilder.of(e)
}
次に、テストは失敗します。つまり、Exception
が として識別されRuntimeException
、非ジェネリック.of
メソッドが呼び出されるため、結果ResultWrapper
には が取り込まれexc
ます。
これは私にとって完全に不可解です。私は、catch (Exception e)
節の中でもe
元の型を保持していると信じています (ログメッセージSystem.out.println(e.getClass().getSimpleName()
はそれが正しいことを示唆しています)。