gpt4 book ai didi

从 catch block 的结果调用具有通用参数的 Java 方法

转载 作者:行者123 更新时间:2023-11-30 08:01:36 25 4
gpt4 key购买 nike

我遇到了一个意外问题,涉及异常捕获和签名中的 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!
}

}

暂且不谈风格不佳的问题(我完全承认更好的方法来完成我在这里所做的事情!),这是一个简化的匿名版本以下业务逻辑:

  • class ResultWrapper 包装对下游服务的调用结果。它要么包含调用的结果,要么包含由此产生的异常
  • WrapperTransformer 类负责以某种方式转换 ResultWrapper(不过,这里的“转换”是空操作)

上面给出的测试失败了。通过调试,我确定这是因为 WrapperBuilder.of(e) 实际上是在调用泛型方法(即 of(final T result))。如果泛型参数是“贪婪的”- RuntimeException T,那(某种程度上)是有道理的,因此该方法是明智的(尽管是无意的)选择。

但是,当 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() 表明这是真的) - 那么如何改变 catch 的“类型”来覆盖通用方法签名?

最佳答案

调用的方法由参数的static 类型定义。

  • 如果你捕获到一个Exception,静态类型是Exception,它不是 RuntimeException 的子类,因此泛型of(Object) 被调用。 (回想一下,T 被翻译成 Object汇编)。
  • 在捕获 RuntimeException 的情况下,静态类型是 RuntimeException,并且由于它确实适合 of(RuntimeException),所以更具体方法被调用。

请注意,e.getClass().getSimpleName() 为您提供动态类型,而不是静态类型。动态类型在编译期间是未知的,而调用哪种方法是在编译期间选择的。

下面是演示相同问题的更简单的代码:

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());
}

在这里,方法foo(Object)被调用,即使x是一个Integer,因为的静态类型>x,编译时已知,是Number,不是Integer的子类。

关于从 catch block 的结果调用具有通用参数的 Java 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37443633/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com