在没有声明的情况下,为什么在某些情况下重新抛出Throwable是合法的?
我希望以下代码在throw t;
上throw t;
编译时错误throw t;
,因为main
没有被声明为抛出Throwable
,但是它成功编译(在Java 1.7.0_45中),并且如果修复了编译时错误,则产生你期望的输出。
public class Test { public static void main(String[] args) { try { throw new NullPointerException(); } catch(Throwable t) { System.out.println("Caught "+t); throw t; } } }
如果Throwable
更改为Exception
它也会编译。
这不会按预期编译:
public class Test { public static void main(String[] args) { try { throw new NullPointerException(); } catch(Throwable t) { Throwable t2 = t; System.out.println("Caught "+t2); throw t2; } } }
这编译:
public class Test { public static void main(String[] args) { try { throwsRuntimeException(); } catch(Throwable t) { System.out.println("Caught "+t); throw t; } } public static void throwsRuntimeException() { throw new NullPointerException(); } }
这不是:
public class Test { public static void main(String[] args) { try { throwsCheckedException(); } catch(Throwable t) { System.out.println("Caught "+t); throw t; } } public static void throwsCheckedException() { throw new java.io.IOException(); } }
这也编译:
public class Test { public static void main(String[] args) throws java.io.IOException { try { throwsIOException(); } catch(Throwable t) { System.out.println("Caught "+t); throw t; } } public static void throwsIOException() throws java.io.IOException { throw new java.io.IOException(); } }
一个更复杂的示例 – 已检查的exception由外部catch块捕获,而不是声明为抛出。 这编译:
public class Test { public static void main(String[] args) { try { try { throwsIOException(); } catch(Throwable t) { System.out.println("Caught "+t); throw t; } } catch(java.io.IOException e) { System.out.println("Caught IOException (outer block)"); } } public static void throwsIOException() throws java.io.IOException { throw new java.io.IOException(); } }
因此,当编译器可以确定捕获的exception总是合法的重新抛出时,似乎有一种特殊情况允许重新抛出exception。 它是否正确? 这在JLS中指定了哪里? 还有其他像这样模糊不清的角落吗?
这由JLS 11.2.2 (强调我的)涵盖:
抛出语句表达式是catch子句C 的最终或有效最终exception参数的throw语句可以抛出exception类E iff:
E是一个exception类,声明C的try语句的try块可以抛出; 和
E的赋值与C的任何可捕获exception类兼容 ; 和
(……)
换句话说,文档中引用的类型E
是可以抛出的类型,而不是捕获它的catch子句参数的类型(可捕获的exception类 )。 它只需要与catch子句参数兼容 ,但在分析中不使用参数的类型。
这就是为什么他们不遗余力地说出一个最终的或有效的最终exception参数 –如果在你的例子中重新分配,分析就会消失。
因为编译器足够聪明,知道不能从try块抛出已检查的exception,因此捕获的Throwable不是必须声明的已检查exception。
请注意,自Java 7以来,这是真的,如果我没有弄错的话。
当你捕获Throwable
或Exception
并且变量实际上是final时,你可以重新抛出相同的变量,编译器将知道你可以在try {} catch
块中抛出哪些已检查的exception。