我遇到了一个意外问题,涉及签名中的异常捕获和 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
是a T
,所以该方法是一个明智的(尽管不是有意的)选择。
但是,当DownstreamWrapper::getResult
方法更改为:
// i.e. explicitly catch RuntimeException, not Exception
} catch (RuntimeException e) {
return WrapperBuilder.of(e)
}
然后测试失败 - 即被Exception
标识为 a RuntimeException
,调用非泛型.of
方法,因此结果ResultWrapper
具有填充的exc
.
这对我来说完全莫名其妙。我相信,即使在一个catch (Exception e)
子句中,它也会e
保留其原始类型(并且日志消息System.out.println(e.getClass().getSimpleName()
表明这是真的) - 那么如何更改 catch 的“类型”覆盖通用方法签名?