【问题标题】:Rethrowing Exception without requiring throws Exception? [duplicate]重新抛出异常而不需要抛出异常? [复制]
【发布时间】:2016-10-21 22:28:00
【问题描述】:

考虑以下代码:

static void main(String[] args) {
  try {
  } catch (Exception e) {
    throw e;
  }
}

This code compiles 无需在方法签名中添加throws Exception。 (用Throwable 代替Exception 的行为也类似)。

我明白为什么它可以安全运行,因为Exception 实际上不能在try 块中抛出,因此不能抛出已检查异常;我很想知道这种行为是在哪里指定的。

这不仅仅是因为永远无法到达throw e:以下代码也可以编译:

static void stillCompilesWithThrownUncheckedException() {
  try {
    throw new NullPointerException();
  } catch (Exception e) {
    throw e;
  }
}

但是如果你抛出一个已检查的异常,它不会像我预期的那样编译:

static void doesNotCompileWithThrownCheckedException() {
  try {
    throw new Exception();
  } catch (Exception e) {
    throw e;  // error: unreported exception Exception; must be caught or declared to be thrown
  }
}

JLS Sec 11.2.2 中,它说:

throw 语句(第 14.18 节)其抛出的表达式具有静态类型 E,并且不是 final 或有效的 final 异常参数,可以抛出 E 或抛出的表达式可以抛出的任何异常类。

我对这句话的解释是throw e可以抛出Exception,因为e的静态类型是Exception。然后,在JLS Sec 11.2.3

如果一个方法或构造函数体可以抛出某个异常类 E,而 E 是一个已检查的异常类并且 E 不是该方法或构造函数的 throws 子句中声明的某个类的子类,则这是一个编译时错误。

但这不是前两种情况下的编译时错误。语言规范中在哪里描述了这种行为?


编辑:在将其标记为骗子后,我将提出后续问题:为什么在第一个示例中throw e; 不被视为不可访问

JLS Sec 14.21 中更容易找到答案:

  • 如果满足以下两个条件,则可以访问 catch 块 C:

    • C 的参数类型要么是未经检查的异常类型,要么是 Exception,要么是 Exception 的超类,或者 try 块中的某些表达式或 throw 语句是可访问的,并且可以抛出类型可分配给 C 参数类型的检查异常. (如果包含它的最里面的语句是可访问的,则表达式是可访问的。)

      有关表达式的正常和突然完成,请参阅 §15.6。

    • try语句中没有更早的catch块A,使得C的参数类型与A的参数类型相同或者是A的子类。

这两个都是真的(它的类型是Exception,并且没有更早的catch块),所以它是“可达的”。我猜想将空的 try 块作为特例调用对于这种勉强有用的构造来说太大了。

【问题讨论】:

  • 当我尝试用空的try 块编译main 方法时,我收到错误incompatible types: Exception cannot be converted to Throwable。您使用的是什么版本的 Java?
  • @EliSadoff 演示在 Ideone 上,所以无论他们使用什么。我想是sun-jdk-8u51
  • @Tunaki 相关,但这只是描述现象,并没有实际给出 JLS 的具体指针。
  • "Why is throwing a checked exception type allowed in this case?" 对此进行了介绍,但我不会把它当作一个骗子来关闭,因为它是针对 Java 7 的,而 Java 8 中的细节可能已经改变。

标签: java


【解决方案1】:

我相信第 11.2.2 节的下一段回答了这个问题:

如果 throw 语句抛出的表达式是 catch 子句 C 的最终或有效最终异常参数,则可以抛出异常类 E iff:

  • E是声明C的try语句的try块可以抛出的异常类;和

因此,throw e;“可以抛出”仅对应的 try 块“可以抛出”的异常,后者由 try 块中的实际语句定义。

显然,空的 try 块不符合任何异常类的“可以抛出”部分的条件。您的第二个示例“可以抛出”NullPointerException,并且由于 catch 块“可以抛出”仅 try 块“可以抛出”的异常,catch 块也只能抛出未经检查的 NullPointerException。

您的第三个示例的 try 块“可以抛出”java.lang.Exception 本身,因此 catch 块“可以抛出”java.lang.Exception,因此 java.lang.Exception 必须被捕获或声明为抛出。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-24
    • 1970-01-01
    • 1970-01-01
    • 2012-06-28
    • 2013-05-24
    • 1970-01-01
    • 1970-01-01
    • 2011-10-10
    相关资源
    最近更新 更多