例外をキャッチして再スローするが、例外ではない


10

私は次のようなコードに遭遇しました:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        System.out.println("Error: " + ex);
        throw ex;
    }
}

void doSomething() {
    throw new RuntimeException();
}

このコードは私を驚かせます。- methodはそれをキャッチしてから再スローするため、をスローrun()できるように見えますが、メソッドがスローするように宣言されておらず、明らかにそうである必要はないからです。このコードは問題なくコンパイルされます(少なくともJava 11では)。ExceptionExceptionException

私の予想でthrows Exception、- run()メソッドで宣言する必要があるでしょう。

追加情報

同様に、if doSomethingがスローするように宣言されている場合、キャッチされて再スローされても、-methodで宣言するIOExceptionだけで済みIOExceptionます。run()Exception

void run() throws IOException {
    try {
        doSomething();
    } catch (Exception ex) {
        System.out.println("Error: " + ex);
        throw ex;
    }
}

void doSomething() throws IOException {
    // ... whatever code you may want ...
}

質問

Javaは通常、明快さを好みますが、この動作の背後にある理由は何ですか?いつもこんな感じでしたか?Java言語仕様の中で、上記のコードスニペットでrun()メソッドが宣言する必要のないものは何throws Exceptionですか?(追加すると、IntelliJ Exceptionはスローされないことを警告します)。


3
面白い。どのコンパイラを使用していますか?それがIDEコンパイラーである場合は、以下を確認してくださいjavac-私はEclipseコンパイラーの方が寛大な場合に遭遇しています。
M.プロホロフ

2
この動作をopenjdk-8で再現できます。特に-source 1.6フラグを使用してコンパイルすると、予想どおりにコンパイルエラーが発生します。ソース互換性7でコンパイルしてもコンパイルエラーは発生しませ
Vogel612

1
Java 7以降、コンパイラはよりスマートになり、スローされる可能性のある実際の例外に対してより多くのチェックを行います。
ミシェルク

2
この質問は重複ではなく、答えは私が提供したリンクにありますIn detail, in Java SE 7 and later, when you declare one or more exception types in a catch clause, and rethrow the exception handled by this catch block, the compiler verifies that the type of the rethrown exception meets the following conditions : 1. 1. The try block is able to throw it. 2. There are no other preceding catch blocks that can handle it. 3. It is a subtype or supertype of one of the catch clause's exception parameters.
ミシェルク

2
現在、マークの重複は間違いなく関連しているが、IMO詳細な十分な答えを提供していません。そこへの回答へのコメントに JLSへのリンクが1つありますが、それ以外に情報はありません。
Simon Forsberg、

回答:


0

JLSあなたの質問であなたが尋ねたように私はスキャンしていませんので、塩の粒でこの答えを取ってください。コメントにしたかったのですが、大きすぎます。


私は時々面白いと思います。javac場合によっては(あなたの場合のように)かなり「賢い」かもしれませんが、後で処理する他の多くのものを残しJITます。この場合、コンパイラーRuntimeExceptionがキャッチできるのはコンパイラーに「わかる」だけです。これは明らかです、それはあなたが投げ込む唯一のものですdoSomething。コードを少し変更した場合:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        Exception ex2 = new Exception();
        System.out.println("Error: " + ex);
        throw ex2;
    }
}

別の動作が表示されます。javacこれは、Exceptionキャッチしたものとは無関係に、投げている新しいものがあることがわかるためです。

しかし、物事は理想とはほど遠いので、次のようにしてコンパイラを「だます」ことができます。

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        Exception ex2 = new Exception();
        ex2 = ex;
        System.out.println("Error: " + ex);
        throw ex2;
    }
}

IMO、ex2 = ex;それが原因で再び失敗することはないはずですが、失敗します。

これがでコンパイルされた場合に備えて javac 13+33


誰かが提供したリンクを読んだところ、catch-blockでキャッチされた例外を再割り当てすると、コンパイラーはスマートになりません。この場合も同様のことが当てはまると思います。コンパイラーは、ex2例外がスローされることを知っています。例外は最初はとして作成されましExceptionたが、次にに再割り当てされているexため、コンパイラーはスマートにできません。
Simon Forsberg、

@SimonForsbergへの情熱を持つ誰かJLSが来て、これを証明するために必要な引用を提供するかもしれません。残念ながらありません。
ユージーン

レコードについて、catchブロックを変更して、キャッチされた例外の再割り当てを含める(ex = ex;)と、ヒューリスティックは適用されなくなりました。この動作は、
7〜11

重複しているこの質問を見てください。これと可能なdupのdupはそれを説明し、JLSにもリンクしています。
ミシェルク
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.