Как работает multi-catch exception handling в Java?
Что проверяют
Знаете ли вы синтаксис из Java 7 и ограничения, которые проверяет компилятор. Следом часто спрашивают, какой тип у переменной исключения внутри такого блока и что такое precise rethrow.
Как отвечать
Multi-catch позволяет обработать несколько несвязанных типов исключений одним блоком: catch (IOException | SQLException e). До Java 7 для этого писали два одинаковых catch или ловили общий Exception, теряя точность.
Ключевые правила:
- неявная
final: параметрeнельзя переприсвоить, строкаe = new IOException()не скомпилируется; - типы не должны быть родственниками:
catch (FileNotFoundException | IOException e)даёт ошибку компиляции, потому что первый тип и так покрывается вторым; - статический тип
e: это ближайший общий супертип альтернатив. УIOException | SQLExceptionэтоException, поэтому методы вродеgetSQLState()без приведения недоступны; - проверяемые исключения: каждое проверяемое исключение в списке должно реально выбрасываться в
try, иначе компилятор сообщит, что оно никогда не бросается; - байткод: генерируется один обработчик с несколькими записями в таблице исключений, код не дублируется.
Хорошо дополнить темой precise rethrow, тоже из Java 7. Если поймать Exception e и выбросить его заново без переприсвоения, компилятор знает реальные типы из try, и метод может объявить throws IOException, SQLException вместо широкого throws Exception.
С try-with-resources и finally multi-catch сочетается как обычный catch. На практике он часто стоит там, где низкоуровневые исключения переводятся в одно доменное.
Где спотыкаются
Перечисляют родителя и потомка в одном multi-catch. Это не предупреждение, а ошибка компиляции.
Пытаются вызвать метод конкретного типа, например getErrorCode() у SQLException. Внутри блока e имеет общий тип, нужен instanceof с pattern matching или отдельный catch.
Используют multi-catch там, где обработка на самом деле разная. Если для IOException нужен повтор, а для SQLException откат транзакции, это два блока, а не один с if внутри.