Java 无法访问的捕获块编译器错误

2022-09-02 23:45:41

为什么在Java中,即使一个没有被抛出,我们也能抓住它,但我们不能抓住它的子类(除了“unchecked”和它的子类)。示例代码:ExceptionRuntimeException

class Test {
    public static void main(String[] args) {
        try {
            // do nothing
        } catch (Exception e) {
            // OK           
        }

        try {
            // do nothing
        } catch (IOException e) {
               // COMPILER ERROR: Unreachable catch block for IOException.
               //This exception is never thrown from the try statement body
        }       
    }
}

有什么想法吗?


答案 1

任何代码都可以抛出 A。换句话说,编译器无法轻松预测哪种代码可以抛出它。A 可以被块捕获。RuntimeExceptionRuntimeExceptioncatch(Exception e)

IOException但是,这是一个已检查的异常 - 只有声明为引发它的方法调用才能这样做。编译器可以(合理地)确信它不可能发生,除非存在声明为抛出它的方法调用。

Java编译器根本不考虑“try块中根本没有代码”的情况 - 它始终允许您捕获未经检查的异常,因为在所有合理的场景中,都会有代码可能会引发未经检查的异常。

从 JLS 的第 14.21 节中可以看出:

捕获块 C 是可访问的,如果以下两个都为真:

  • try 块中的某些表达式或 throw 语句是可访问的,并且可以引发一个异常,其类型可分配给 catch 子句 C 的参数(如果包含表达式的最内层语句可访问,则该表达式被视为可访问。
  • 在 try 语句中没有更早的 catch 块 A,使得 C 的参数类型与 A 的参数类型相同或为 A 参数类型的子类。

可以说,编译器应该意识到,在你的第一种情况下,try块中没有表达式......对我来说,这看起来仍然是一个无法触及的捕获条款。

编辑:如评论中所述,第14.20节包含以下内容:

如果子句捕获已检查的异常类型 E1,但不存在已检查的异常类型 E2,因此以下所有情况都保持不变,则这是一个编译时错误:catch

  • E2 <: E1
  • 与子句对应的块可以抛出 E2trycatch
  • 立即封闭的 try 语句前面的块没有捕获 E2E2 的超类型。catch

除非 E1 是类异常。

所以看起来这就是你实际上犯规的东西,但是规范并不像14.21中无法到达的捕获块那样清晰。


答案 2

仅当编译器预测代码中可能存在引发 IOException 的内容时,才能捕获 IO 异常。因此,您会收到一条警告,指出 IO 异常永远不会从 try 语句正文中引发(因为 try 的正文中没有任何内容)。


推荐