重新抛出异常而不需要抛出异常?

时间:2016-10-21 22:28:01

标签: java

请考虑以下代码:

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,并且不是最终或有效的最终异常参数,可以抛出E或抛出的表达式可以抛出的任何异常类。

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

  

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

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

编辑:将其标记为欺骗,我打算提出后续问题:为什么在第一个示例中<{1}}被认为无法访问

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

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

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

           

      表达式的正常和突然完成见§15.6。

    •   
    • try语句中没有先前的catch块A,因此C的参数类型与A参数类型的子类相同。

    •   
  •   

这两个都是正确的(它的类型为throw e;,并且没有先前的catch块),所以它是“可达的”。我想将一个空的Exception块作为特殊情况调用的努力对于这种边缘有用的构造来说太过分了。

1 个答案:

答案 0 :(得分:1)

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

  

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

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

因此,throw e;“只能抛出”相应的try-block“可以抛出的异常,”后者由try-block中的实际语句定义。

显然,空的try-block不符合任何异常类的“can throw”部分。你的第二个例子“可以抛出”NullPointerException,并且因为catch-block“只能抛出”try-block“可以抛出”的异常,所以catch-block也只能抛出未经检查的NullPointerException。

你的第三个例子的try-block“可以抛出”java.lang.Exception本身,因此catch-block“可以抛出”java.lang.Exception,因此必须捕获或声明java.lang.Exception被抛出。