[英]Java method with Generic argument called from result of catch block
I've run into an unexpected issue involving Exception catching and Java generics in signatures. 我遇到了一个意外的问题,涉及异常捕获和签名中的Java泛型。 Without further ado, the code in question (explanation follows):
事不宜迟,有问题的代码(如下解释):
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!
}
}
Leaving aside, for the moment, questions of bad style (I completely acknowledge that there are better ways to do do what I'm doing here!), this is a trimmed down and anonymised version of the following business logic: 暂且不说,就目前而言,坏作风问题(!我完全承认, 有更好的方法去做做我在这里做什么),这是下列业务逻辑的下调和匿名版本:
ResultWrapper
wraps the result of a call to a downstream service. ResultWrapper
类包装对下游服务的调用结果。 It either contains the result of the call, or the resulting exception WrapperTransformer
is responsible for transforming the ResultWrapper in some way (though, here, the "transformation" is a no-op) WrapperTransformer
类负责以某种方式转换ResultWrapper(尽管这里的“转换”是禁止操作的) The test given above fails. 上面给出的测试失败。 From debugging, I have determined that this is because
WrapperBuilder.of(e)
is, in fact, calling the generic method (ie of(final T result)
). 通过调试,我确定这是因为
WrapperBuilder.of(e)
实际上正在调用泛型方法(即of(final T result)
)。 That (sort of) makes sense, if generic arguments are "greedy" - a RuntimeException
is a T
, so that method is a sensible (though unintended) choice. 如果泛型参数是“贪婪的”,则这(某种)是有意义的
RuntimeException
是 T
,因此该方法是明智的(尽管是意外的)选择。
However, when the DownstreamWrapper::getResult
method is changed to: 但是,当
DownstreamWrapper::getResult
方法更改为:
// i.e. explicitly catch RuntimeException, not Exception
} catch (RuntimeException e) {
return WrapperBuilder.of(e)
}
then the test fails - ie the Exception
is identified as a RuntimeException
, the non-generic .of
method is called, and so the resulting ResultWrapper
has a populated exc
. 然后测试失败-即
Exception
被识别为RuntimeException
,非泛型.of
方法被调用,并且因此所得ResultWrapper
具有填充exc
。
This is completely baffling to me. 这完全让我感到困惑。 I believe that, even inside a
catch (Exception e)
clause, e
retains its original type (and logging messages System.out.println(e.getClass().getSimpleName()
suggest that is true) - so how can changing the "type" of the catch override the generic method signature? 我相信,即使在
catch (Exception e)
子句中, e
仍保留其原始类型(并且记录消息System.out.println(e.getClass().getSimpleName()
表示这是事实)-因此,如何更改“捕获的“类型”会覆盖通用方法签名?
The method that is called is defined by the static type of the argument. 调用的方法由参数的静态类型定义。
Exception
, the static type is Exception
, which is not a subclass of RuntimeException
, so the generic of(Object)
is called. Exception
的情况下,静态类型为Exception
,它不是RuntimeException
的子类,因此将调用泛型of(Object)
。 (Recall that T
is translated to Object
in compilation). T
在编译时已转换为Object
)。 RuntimeException
, the static type is RuntimeException
, and since it does fit of(RuntimeException)
, the more specific method is called. RuntimeException
的情况下,静态类型为RuntimeException
,并且由于它确实适合of(RuntimeException)
,因此将调用更具体的方法。 Note that e.getClass().getSimpleName()
is giving you the Dynamic type, and not the static one. 请注意,
e.getClass().getSimpleName()
为您提供了动态类型,而不是静态类型。 The dynamic type is unknown during compilation, while which method is invoked is chosen during compile time. 动态类型在编译期间是未知的,而在编译期间选择调用哪种方法。
Here is a simpler code that demonstrates the same issue: 这是一个演示相同问题的简单代码:
public static void foo(Object o) {
System.out.println("foo(Object)");
}
public static void foo(Integer n) {
System.out.println("foo(Integer)");
}
public static void main (String[] args) throws java.lang.Exception {
Number x = new Integer(5);
foo(x);
System.out.println(x.getClass().getSimpleName());
}
In here, the method foo(Object)
is called, even though x
is an Integer
, because the static type of x
, which is known in compile time, is Number
, and is not a subclass of Integer
. 在这里,即使
x
是Integer
,也将调用方法foo(Object)
,因为在编译时已知的x
的静态类型是Number
,而不是Integer
的子类。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.