【问题标题】:Why does a java.lang.reflect.Proxy require declaring checked exceptions when normal method invocation does not?为什么 java.lang.reflect.Proxy 需要声明检查的异常,而正常的方法调用不需要?
【发布时间】:2019-08-02 17:22:13
【问题描述】:

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

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

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

由始终抛出 IOExceptionInvocationHandler 支持的基于 Proxy 的实例产生两种行为:

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

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

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


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

之类的模式中很常见
} catch (e: IOException) {
  throw new UncheckedIOException(e);
}

¹ 要么使用“偷偷摸摸”技术,用 Kotlin 等没有检查异常的语言编写它,要么直接在字节码中实现类。

【问题讨论】:

  • 我猜这只是设计选择,java 喜欢添加这样奇怪的东西。想法可能是为了防止纯 java 代码在不应该发生的地方得到检查异常 - 因为当时不存在偷偷摸摸的 throw(代理 API 是在泛型存在之前创建的),并且其他编译器和手动字节码操作不是什么这也是意料之中的。 (仪器后来也加了)
  • 我不完全确定答案会是什么样子。但是导致UndeclaredThrowableException 被抛出的代码一般是根据声明的 异常生成的。只有被声明的才会被正常接收。那些 not 声明的将被视为“any throwable”,作为最后的手段被捕获和包装。相关代码是例如在github.com/JetBrains/jdk8u_jdk/blob/master/src/share/classes/…
  • 您的脚注证明了您所引用的技术在任何一种情况下都不受基本语言的支持。当然,任何偷偷摸摸的技术都会偷偷溜过去。不清楚您在这里真正要问的是什么。
  • @user207421 语言选择无关紧要。我可以重写由字节码构成的问题,并且不需要脚注。这是虚拟机的行为。

标签: java reflection


【解决方案1】:

我会说这只是一个设计选择,就像许多其他人一样。 想法可能是防止纯 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-20
    • 2014-07-20
    • 2022-01-14
    • 1970-01-01
    • 2014-12-29
    • 2016-09-07
    相关资源
    最近更新 更多