Eine Ausnahme abfangen und erneut werfen, aber es ist keine Ausnahme

10

Ich bin auf Code gestoßen, der ungefähr so ​​aussieht:

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

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

Dieser Code überrascht mich, weil es so aussieht, als ob die run()-Methode in der Lage ist, ein zu werfen Exception, da es es fängt Exceptionund dann erneut wirft, aber die Methode ist nicht zum Werfen deklariert Exceptionund muss es anscheinend nicht sein. Dieser Code wird einwandfrei kompiliert (zumindest in Java 11).

Meine Erwartung wäre, dass ich throws Exceptionin der run()Methode deklarieren müsste .

Zusatzinformation

In ähnlicher Weise muss, wenn doSomethingzum Werfen deklariert wird, IOExceptionnur IOExceptionin der Methode deklariert werden run(), obwohl Exceptiones gefangen und erneut geworfen wird.

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 ...
}

Frage

Java mag normalerweise Klarheit. Was ist der Grund für dieses Verhalten? War es schon immer so? Was in der Java-Sprachspezifikation erlaubt es der run()Methode, nicht throws Exceptionin den obigen Codefragmenten zu deklarieren ? (Wenn ich es hinzufügen würde, warnt mich IntelliJ, dass Exceptiones nie geworfen wird).

Simon Forsberg
quelle
3
Interessant. Welchen Compiler verwenden Sie? Wenn es sich um einen IDE-Compiler handelt, wenden Sie sich an javac- Ich bin auf Fälle gestoßen, in denen der Eclipse-Compiler milder war.
M. Prokhorov
2
Ich kann dieses Verhalten auf openjdk-8 reproduzieren. Insbesondere das Kompilieren mit dem -source 1.6Flag führt erwartungsgemäß zu einem Kompilierungsfehler. Das Kompilieren mit
Quellkompatibilität
1
Der Compiler scheint seit Java 7 intelligenter zu sein und überprüft mehr die tatsächliche Ausnahme, die ausgelöst werden könnte.
Michalk
2
Diese Frage ist kein Duplikat und die Antwort kann in dem Link gefunden werden, den ich bereitgestellt habeIn 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.
michalk
2
Das aktuell markierte Duplikat ist definitiv relevant, bietet aber keine ausreichend detaillierte Antwort IMO. Es gibt einen Link zum JLS in den Kommentaren zur Antwort dort, außerdem keine Informationen.
Simon Forsberg

Antworten:

0

Ich habe das nicht durchgesehen, JLSwie Sie in Ihrer Frage gefragt haben, also nehmen Sie diese Antwort bitte mit einem Körnchen Salz. Ich wollte einen Kommentar abgeben, aber er wäre zu groß gewesen.


Ich finde es manchmal lustig, wie javacziemlich "klug" in einigen Fällen (wie in Ihrem Fall) ist, aber viele andere Dinge müssen später erledigt werden JIT. In diesem Fall kann der Compiler nur "erkennen", dass nur a RuntimeExceptionabgefangen wird. Dies ist offensichtlich, es ist das einzige, was Sie einwerfen doSomething. Wenn Sie Ihren Code leicht ändern in:

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

Sie werden ein anderes Verhalten sehen, weil Sie jetzt erkennen javackönnen, dass es ein neues gibt Exception, das Sie werfen, das nichts mit dem zu tun hat, das Sie gefangen haben.

Aber die Dinge sind alles andere als ideal, Sie können den Compiler noch einmal "austricksen" über:

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

IMO, weil ex2 = ex;es nicht wieder scheitern sollte, aber es tut.

Nur für den Fall, dass dies mit kompiliert wurde javac 13+33

Eugene
quelle
Ich habe in einem Link gelesen, dass jemand angegeben hat, dass der Compiler nicht intelligent sein kann, wenn Sie die abgefangene Ausnahme im catch-Block neu zuweisen. Ich gehe davon aus, dass in diesem Fall etwas Ähnliches gilt. Der Compiler weiß, dass die ex2Ausnahme ausgelöst wird. Sie wurde ursprünglich als erstellt Exception, wird dann aber neu zugewiesen ex, und daher kann der Compiler nicht intelligent sein.
Simon Forsberg
@ SimonForsberg Jemand, der eine Leidenschaft für hat, JLSkönnte kommen und die erforderlichen Zitate liefern, um dies zu beweisen; Leider habe ich sie nicht.
Eugene
Wenn ich den catch-Block so ändere, dass er eine neu zugewiesene Ausnahme für sich selbst enthält ( ex = ex;), wird die Heuristik für den Datensatz nicht mehr angewendet. Dieses Verhalten scheint für alle Quellenebenen von 7 bis 11 und wahrscheinlich 13
Vogel612
Schauen Sie sich diese Frage an, die auch ein Duplikat ist. Dieser und der Dup des möglichen Dup erklären es und verlinken auch auf JLS.
Michalk