Подтвердить что ты не робот

Почему try..finally блокировать не регистрировать исходное исключение как подавленное?

Со следующим кодом:

try {
    throw new RuntimeException ("main");
}
finally {
    throw new RuntimeException ("finally");
}

Получаю этот результат:

Exception in thread "main" java.lang.RuntimeException: finally
        at test.main(test.java:12)

Однако, с добавлением исключенных исключений в Java 7, не было бы логичным для языка регистрировать оригинальное "основное" исключение, как подавленное, когда блок finally сам сбой выходит из строя с исключением? В настоящее время мне приходится вручную подражать этому:

try {
    throw new RuntimeException ("main");
}
catch (RuntimeException exception) {
    try {
        throw new RuntimeException ("finally");
    }
    catch (RuntimeException exception2) {
        exception2.addSuppressed (exception);
        throw exception2;
    }
}

чтобы получить более полезный (для понимания того, что происходит):

Exception in thread "main" java.lang.RuntimeException: finally
        at test.main(test.java:13)
        Suppressed: java.lang.RuntimeException: main
                at test.main(test.java:9)

EDIT: Чтобы выяснить, что мне интересно. Текущая версия Java - 8, подавленные исключения - не новая функция. Но try..finally все еще не включает их. Есть ли что-то, что мешает этому?

4b9b3361

Ответ 1

Поскольку try-with-resources является синтаксическим сахаром, а компилятор Java не расширяет обычные блоки try-finally таким же образом.

Взгляните на следующий код:

try(FileInputStream fstream = new FileInputStream("test")) {
    fstream.read();
}

При компиляции и затем декомпиляции (с использованием IntelliJ IDEA) это выглядит так:

FileInputStream fstream = new FileInputStream("test");
Throwable var2 = null;

try {
    fstream.read();
} catch (Throwable var19) {
    var2 = var19;
    throw var19;
} finally {
    if(fstream != null) {
        if(var2 != null) {
            try {
                fstream.close();
            } catch (Throwable var17) {
                var2.addSuppressed(var17);
            }
        } else {
            fstream.close();
        }
    }
}

В то время как этот код:

FileInputStream fstream = new FileInputStream("test");
try {
    fstream.read();
} finally {
    fstream.close();
}

Выглядит точно так же, когда компилируется и декомпилируется.

Теперь можно было бы сделать так, что все блоки finally должны быть расширены так же, как это сделано выше, но по какой-либо причине это либо было пропущено, либо принято против.

Я предлагаю вам открыть запрос функции, потому что я считаю это разумной функцией.

Ответ 2

Это не авторитетный ответ, но похоже, что такое изменение либо сломало бы совместимость, либо try-with-resources и try-finally было бы несовместимо друг с другом.

Семантика в try-with-resources заключается в том, что исключение, выделенное из блока try, распространяется, за исключением того, что в finally зарегистрировано исключение, зарегистрированное как подавленное. Это имеет смысл с практической точки зрения, вы хотите поймать свое "реальное" исключение, а затем, возможно, имеете дело с тем, что ресурс не закрывается, если вы хотите.

Но в try-finally это исключение, которое распространяется в finally, которое распространяется (в то время как один из try "проглатывается" ), и поэтому у нас есть выбор из трех плохих решений:

  • Переверните логику try-finally, чтобы выровнять ее с try-with-resources и испортить код (или, скорее, ошибка) со всем предыдущим кодом.
  • Продолжайте распространять исключение finally с исключением try, зарегистрированным как подавленное, что делает две конструкции несогласованными друг с другом.
  • Просто оставьте все как есть.

Субъективно я вижу 3 менее плохим, чем 1 или 2, хотя довольно легко утверждать иначе. Я подозреваю, однако, что это была дилемма, с которой сталкивались разработчики языка, и им удалось выбрать вариант 3.