Поймать исключение и выбросить его, но это не исключение

10

Я наткнулся на код, который выглядит примерно так:

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

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

Этот код удивляет меня, потому что он выглядит так, как будто run()-method способен генерировать Exception, так как он перехватывает Exceptionи затем перебрасывает его, но метод не объявлен как throw Exceptionи, очевидно, не нуждается в этом. Этот код прекрасно компилируется (по крайней мере, в Java 11).

Я ожидал бы, что мне придется объявить throws Exceptionв run()методе.

Дополнительная информация

Аналогичным образом, если doSomethingобъявлено, что оно выбрасывает, 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-компилятор был более снисходительным.
М. Прохоров
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.
michalk
2
В настоящее время отмечено дубликат , безусловно , актуален, но не дает достаточно детализировано ответа ИМО. Там есть одна ссылка на JLS в комментариях к ответу там, кроме этого нет информации.
Саймон Форсберг

Ответы:

0

Я не просканировал, JLSкак вы задали в своем вопросе, поэтому, пожалуйста, ответьте на этот вопрос солью. Я хотел сделать это комментарием, но это было бы слишком большим.


Время от времени я нахожу забавным, как javacдовольно «умный» в некоторых случаях (как в вашем случае), но оставляет много других вещей, которые будут обработаны позже JIT. В этом случае просто компилятор "может сказать", что RuntimeExceptionбудет пойман только a . Это очевидно, это единственное, что вы добавляете 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;
    }
}

ИМО, из-за ex2 = ex; этого не должно снова подвести, но это так.

На всякий случай это было скомпилировано с javac 13+33

Евгений
источник
Я прочитал в какой-то ссылке, что кто-то при условии, что если вы переназначите перехваченное исключение в блоке catch, то компилятор не сможет быть умным. Я предполагаю, что нечто подобное применимо в этом случае. Компилятор знает, что ex2будет сгенерировано исключение, он изначально был создан как, Exceptionно затем переназначен ex, и поэтому компилятор не может быть умным.
Саймон Форсберг
@SimonForsberg JLSможет прийти тот, у кого есть страсть, и предоставит необходимые цитаты, чтобы доказать это; к сожалению у меня их нет.
Евгений
Для записи, когда я изменяю блок catch, чтобы он содержал переназначение перехваченного исключения для себя ( ex = ex;), эвристика больше не применяется. Такое поведение, кажется, применяется для всех уровней источника с 7 по 11 и, вероятно, 13
Vogel612
Посмотрите на этот вопрос, который также является дубликатом. Этот и дубликат возможного дубликата объясняет это, а также ссылки на JLS.
Михалк