The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:
Иногда мы повторно выбрасываем исключения. В Java мы делаем это чаще, чем в других языках, потому что в ней есть проверяемые исключения. Иногда нам приходится перехватывать и повторно выбрасывать несколько исключений, возникших из разных мест в методе. В Java 7 была введена группировка разных типов исключений в одном блоке catch
. Но даже без группировки можно просто перехватить IOException
или даже Exception
и предоставить единый блок catch
для всех типов и всех источников (методов, где возникают исключения). Недавно я понял, что это плохая практика. Вот почему.
Рассмотрим следующий метод на Java (я использую Apache Commons IO):
byte[] read(String uri) {
try {
return IOUtils.toByteArray(
new URL(uri).openStream()
);
} catch (IOException ex) {
throw new IllegalArgumentException(ex);
}
}
Это не идеально. Давайте перепишем его, чтобы добавить больше контекста ошибки, как было предложено ранее:
byte[] read(String uri) {
try {
return IOUtils.toByteArray(
new URL(uri).openStream()
);
} catch (IOException ex) {
throw new IllegalArgumentException(
String.format(
"Failed to read from '%s'",
uri
),
ex
);
}
}
Здесь исключение может быть сгенерировано в трех местах:
В конструкторе
java.net.URL
С помощью метода
openStream()
С помощью метода
toByteArray
Не важно, кто бросает, мы перехватываем это в том же блоке catch
и повторно бросаем с тем же сообщением. Я считаю, что это плохо, потому что контекст ошибки, предоставленный повторным бросанием, менее сфокусирован на произошедшей проблеме.
Я бы порекомендовал провести рефакторинг (я не закрываю входной поток, что неправильно, но это тема для отдельного обсуждения).
byte[] read(String uri) {
URL url;
try {
url = new URL(uri);
} catch (MalformedURLException ex) {
throw new IllegalArgumentException(
String.format(
"Failed to parse the URI '%s'",
uri
),
ex
);
}
InputStream stream;
try {
stream = url.openStream();
} catch (IOException ex) {
throw new IllegalArgumentException(
String.format(
"Failed to open the stream for '%s'",
uri
),
ex
);
}
try {
return IOUtils.toByteArray(stream);
} catch (IOException ex) {
throw new IllegalArgumentException(
String.format(
"Failed to read the stream for '%s'",
uri
),
ex
);
}
}
Этот код гораздо длиннее, но в то же время удобнее для отладки, тестирования и использования в режиме продукции. Блок catch
способен лучше объяснить ситуацию и предоставить более полный контекст в перебрасываемом исключении, поскольку он занимается только одним случаем.
Таким образом, правило, которое я предлагаю: если исключение перехвачено, каждый инициатор должен иметь свой собственный блок catch
.
Очевидно, я считаю, что объединение типов исключений в одном блоке catch
— это плохая практика.
Translated by ChatGPT gpt-3.5-turbo/35 on 2023-09-08 at 16:28