2

シグニチャの例外キャッチと 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()はそれが正しいことを示唆しています)。

4

1 に答える 1