Почему 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
все еще не включает их. Есть ли что-то, что мешает этому?
Ответы
Ответ 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.