【问题标题】:Why is it legal to re-throw a Throwable in certain cases, without declaring it?为什么在某些情况下重新抛出 Throwable 而不声明它是合法的?
【发布时间】:2014-05-10 13:27:33
【问题描述】:

我希望下面的代码会在 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();
    }
}

一个更复杂的例子 - 被检查的异常被外部的 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();
    }
}

因此,当编译器可以确定捕获的异常总是合法地重新抛出时,似乎存在允许重新抛出异常的特殊情况。这个对吗? JLS 在哪里指定?是否还有其他类似这样的晦涩的极端案例?

【问题讨论】:

    标签: java


    【解决方案1】:

    JLS 11.2.2 涵盖了这一点(强调我的):

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

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

    • E 与 C 的任何可捕获异常类都兼容;和

    (...)

    换句话说,文档中引用的类型E可以抛出的类型,而不是捕获它的catch子句参数的类型(catchable异常类)。它只需要与 catch 子句参数赋值兼容,但参数的类型不用于分析。

    这就是为什么他们不遗余力地说一个最终或有效的最终异常参数--如果您的示例中的t 被重新分配,分析就会消失。 p>

    【讨论】:

      【解决方案2】:

      因为编译器足够聪明,知道不能从 try 块中抛出已检查异常,因此捕获的 Throwable 不是必须声明的已检查异常。

      请注意,如果我没记错的话,从 Java 7 开始就是这样。

      【讨论】:

      • @PeterLawrey:不,我刚试过(我猜你的意思是catch(final Throwable t))。我的猜测是,这与 Java 7 中引入的 Multicatch 相关。
      【解决方案3】:

      当您捕获 ThrowableException 并且变量实际上是最终变量时,您可以重新抛出相同的变量,编译器将知道您可能在 try {} catch 块中抛出了哪些已检查异常.

      【讨论】:

        猜你喜欢
        • 2013-05-13
        • 1970-01-01
        • 2019-12-27
        • 1970-01-01
        • 1970-01-01
        • 2021-11-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多