Ловити виняток і скинути його, але це не виняток


10

Я натрапив на код, виглядаючи приблизно так:

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

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

Цей код мене дивує, оскільки схоже, що run()-метод здатний кинути Exception, оскільки він ловить, Exceptionа потім перезавантажує його, але метод не оголошується кинутим 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 дозволяє run()методу не потрібно оголошувати throws Exceptionу фрагментах коду вище? (Якщо я додам, IntelliJ попереджає мене, що Exceptionніколи не кидається).


3
Цікаво. Який компілятор ви використовуєте? Якщо це компілятор IDE, то перевірте javac- я стикався з випадками, коли компілятор Eclipse був більш м'яким.
М. Прохоров

2
Я можу відтворити таку поведінку на openjdk-8. Помітно компіляція з -source 1.6прапором викликає помилку компіляції, як очікувалося. Компіляція із сумісністю із джерелами 7 не призводить до помилки компіляції
Vogel612

1
здається, що компілятор розумніший з Java 7 і робить більше перевірок на фактичні винятки, які можуть бути кинуті.
michalk

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може прийти хтось, хто має пристрасть, і надав необхідні цитати, щоб довести це; на жаль, я їх не маю.
Євген

Для запису, коли я змінюю блок лову, щоб він містив перепризначення вилученого винятку собі ( ex = ex;), евристика більше не застосовується. Така поведінка, схоже, застосовується для всіх рівнів джерел від 7 до 11 та, ймовірно, 13
Vogel612

Погляньте на це питання, яке також є дублікатом. Цей і дублій можливого копію пояснює це, а також посилається на JLS.
michalk
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.