TL; DR
посылка
- Исключения времени выполнения должны создаваться, когда ошибка неустранима: когда ошибка находится в коде и не зависит от внешнего состояния (таким образом, восстановление будет исправлять код).
- Проверенные исключения должны генерироваться, когда код верен, но внешнее состояние не соответствует ожидаемому: нет сетевого подключения, файл не найден или поврежден и т. Д.
Заключение
Мы можем перебросить проверенное исключение как исключение времени выполнения, если распространяющийся или интерфейсный код предполагает, что базовая реализация зависит от внешнего состояния, а это явно не так.
В этом разделе обсуждается тема, когда должно быть выброшено одно из исключений. Вы можете перейти к следующей горизонтальной полосе, если хотите просто прочитать более подробное объяснение заключения.
Когда уместно выдать исключение времени выполнения? Вы генерируете исключение времени выполнения, когда становится ясно, что код неверен и что восстановление целесообразно путем изменения кода.
Например, целесообразно выдать исключение времени выполнения для следующего:
float nan = 1/0;
Это создаст деление на ноль исключений времени выполнения. Это уместно, потому что код неисправен.
Или, например, вот часть HashMap
конструктора:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
// more irrelevant code...
}
Чтобы зафиксировать начальную емкость или коэффициент загрузки, целесообразно отредактировать код, чтобы убедиться, что в него передаются правильные значения. Это не зависит от того, работает ли какой-нибудь удаленный сервер, от текущего состояния диска, файл или другая программа. То, что конструктор вызывается с недопустимыми аргументами, зависит от правильности вызывающего кода, будь то неправильное вычисление, которое привело к неверным параметрам или неуместному потоку, который пропустил ошибку.
Когда уместно бросить проверенное исключение? Вы генерируете проверенное исключение, когда проблема может быть исправлена без изменения кода. Или, говоря другими словами, вы генерируете проверенное исключение, когда ошибка связана с состоянием, а код верен.
Теперь слово «выздоровление» здесь может быть хитрым. Это может означать, что вы найдете другой способ достижения цели: например, если сервер не отвечает, вам следует попробовать следующий сервер. Если такое восстановление возможно для вашего случая, то это замечательно, но это не единственное, что означает восстановление - восстановление может просто отображать диалоговое окно с сообщением об ошибке, объясняющее, что произошло, или, если это серверное приложение, то это может быть отправив электронное письмо администратору или даже просто записав ошибку надлежащим образом и кратко.
Давайте возьмем пример, который был упомянут в ответе mrmuggles:
public void dataAccessCode(){
try{
..some code that throws SQLException
}catch(SQLException ex){
throw new RuntimeException(ex);
}
}
Это не правильный способ обработки проверенного исключения. Простая неспособность обработать исключение в области действия этого метода не означает, что приложение должно быть аварийно завершено. Вместо этого целесообразно распространить его на более высокую область, например:
public Data dataAccessCode() throws SQLException {
// some code that communicates with the database
}
Что учитывает возможность восстановления абонентом:
public void loadDataAndShowUi() {
try {
Data data = dataAccessCode();
showUiForData(data);
} catch(SQLException e) {
// Recover by showing an error alert dialog
showCantLoadDataErrorDialog();
}
}
Проверенные исключения являются инструментом статического анализа, они дают понять программисту, что может пойти не так в определенном вызове, не требуя от них изучения реализации или прохождения процесса проб и ошибок. Это позволяет легко гарантировать, что никакие части потока ошибок не будут проигнорированы. Возврат проверенного исключения в качестве исключения времени выполнения работает с этой трудоемкой функцией статического анализа.
Также стоит упомянуть, что вызывающий уровень имеет лучший контекст более сложной схемы вещей, как было продемонстрировано выше. Причин для этого может быть много dataAccessCode
, конкретная причина вызова видна только вызывающей стороне - таким образом, он может принять лучшее решение при правильном восстановлении после сбоя.
Теперь, когда мы разобрались с этим различием, мы можем перейти к выводу, когда можно перебросить проверенное исключение как исключение времени выполнения.
Учитывая вышесказанное, когда уместно выбрасывать проверенное исключение как RuntimeException? Когда используемый вами код предполагает зависимость от внешнего состояния, но вы можете четко утверждать, что он не зависит от внешнего состояния.
Учтите следующее:
StringReader sr = new StringReader("{\"test\":\"test\"}");
try {
doesSomethingWithReader(sr); // calls #read, so propagates IOException
} catch (IOException e) {
throw new IllegalStateException(e);
}
В этом примере код распространяется, IOException
потому что API Reader
предназначен для доступа к внешнему состоянию, однако мы знаем, что StringReader
реализация не имеет доступа к внешнему состоянию. В этой области, где мы, безусловно, можем утверждать, что части, участвующие в вызове, не обращаются к IO или любому другому внешнему состоянию, мы можем безопасно перевести исключение в качестве исключения времени выполнения без удивительных коллег, которые не знают о нашей реализации (и, возможно, при условии, что код доступа к IO выдает IOException
).
Причина строгой проверки внешних зависимых от состояния исключений заключается в том, что они недетерминированы (в отличие от логически зависимых исключений, которые будут предсказуемо воспроизводиться каждый раз для версии кода). Например, если вы попытаетесь разделить на 0, вы всегда получите исключение. Если вы не разделите на 0, вы никогда не создадите исключение, и вам не нужно обрабатывать этот случай исключения, потому что это никогда не произойдет. В случае доступа к файлу, однако, одноразовое выполнение не означает, что вы добьетесь успеха в следующий раз - пользователь мог изменить разрешения, другой процесс мог удалить или изменить его. Таким образом, вы всегда должны обрабатывать этот исключительный случай, или у вас, вероятно, есть ошибка.