为什么 java.lang.reflect.Proxy 要求声明已检查的异常,而普通方法调用不需要?



Java 的Proxy要求在当前正在调用的接口方法上声明从后备InvocationHandler抛出的任何已检查异常。

例如,给定一个测试接口,

interface Foo {
void bar1();
void bar2() throws IOException;
}

由始终引发IOExceptionInvocationHandler支持的基于Proxy的实例会产生两种行为:

  • bar1()会抛出一个未经检查的UndeclaredThrowableExceptionIOException是其原因。
  • bar2()将直接抛出IOException

现在,使用普通类实现此接口,并从两个方法¹抛出IOException。任一方法的调用方都将收到直接抛出的IOException

为什么Proxy在运行时强制检查异常,而 VM 的其他部分似乎没有这样做?


注意:这种情况与强制包装已检查异常以抛出它们的方法明显不同,这在模式中很常见,例如

} catch (e: IOException) {
throw new UncheckedIOException(e);
}

¹ 要么使用"偷偷扔"技术,要么用没有检查异常的语言(如 Kotlin)编写它,要么直接在字节码中实现类。

我想说这只是一个设计选择,就像许多其他选择一样。 想法是可能防止纯 java 代码在不可能的地方检查异常 - 因为当时不存在偷偷摸摸的抛出(代理 API 是在泛型存在之前创建的,并且偷偷摸摸的抛出工作归功于泛型类型擦除),其他编译器和手动字节码操作也不是预期的。(后来还添加了检测) 因此,当创建代理类时,没有人会期望从未检查的方法中获得检查异常,因此让它以相同的方式对代理类工作听起来很自然。

仍然在 java 中的想法是不允许随机抛出检查的异常,JVM 不会在较低的杠杆上阻止这种情况是另一回事 - 但语言应该尽可能保持一致。
检查异常是java规范的一部分,而不是jvm本身,所以其他语言可以做任何他们想做的事情 - 但代理类是java sdk的一部分,所以它们应该遵循java规范。 https://docs.oracle.com/javase/specs/jls/se12/html/jls-11.html#jls-11.2.3

最新更新