Java 的Proxy
要求在当前正在调用的接口方法上声明从后备InvocationHandler
抛出的任何已检查异常。
例如,给定一个测试接口,
interface Foo {
void bar1();
void bar2() throws IOException;
}
由始终引发IOException
的InvocationHandler
支持的基于Proxy
的实例会产生两种行为:
bar1()
会抛出一个未经检查的UndeclaredThrowableException
,IOException
是其原因。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