Разве это не хорошая практика для обработки исключений времени выполнения в коде?

11

Я работаю над Java-приложением и вижу, что исключения во время выполнения обрабатываются во многих местах. Например,

try {
    // do something
} catch(NullPointerException e) {
    return null;
}

Мои вопросы, когда это хорошая практика для обработки исключений во время выполнения? Когда исключения следует оставлять необработанными?

Винот Кумар СМ
источник
7
-1: Нет единого ответа на такой широкий и расплывчатый вопрос. Невозможно дать однозначный ответ на такой несфокусированный вопрос, как этот. Этот вопрос ужасен. Пожалуйста, приведите конкретные примеры того, где у вас есть сомнения.
С.Лотт
@ S.Lott Я как-то не согласен в этом случае, так как кажется, что есть подмножество программистов, которые думают, что в этом случае нет «Хорошей» Рациональности.
SoylentGray
2
@Chad: "это тот случай, когда нет" Хорошей "Рациональности". Это может быть правдой. Но единственный возможный ответ должен быть «это зависит». Поэтому вопрос кажется ошибочным.
С.Лотт

Ответы:

18

Это зависит.

Например, Integer#parseIntthrows NumberFormatException(который является RTE), если предоставленная строка не может быть проанализирована. Но, конечно же, вы не хотите, чтобы ваше приложение зависало только потому, что пользователь написал «x» в текстовое поле, предназначенное для целых чисел? И как вы узнаете, может ли строка быть проанализирована, если вы сначала не попытаетесь ее проанализировать? Таким образом, в этом случае RTE - это просто сигнал ошибки, который должен вызвать какое-то сообщение об ошибке. Можно утверждать , что это должно быть проверено исключение, но что вы можете сделать - это не так .

Joonas Pulakka
источник
2
Я согласен с «Это зависит», но ваш пример синтаксического анализа, скорее всего, является примером плохого дизайна API. Integer#parseIntдолжен действительно вернуть Maybe<Integer>вместо этого и не выдавать никаких исключений вообще.
Йорг Миттаг
3
@ Jörg W Mittag: Я согласен, что это плохой дизайн API, но это реальный мир, с которым нам приходится иметь дело. Как правильно обрабатывать throwables / возвращаемые значения, зависит от того, как на самом деле работает мир , а не от того, как он оптимально должен работать :-)
Joonas Pulakka
Дело в том, что вы можете сделать что-то значимое для ожидаемого условия (пользовательский ввод не целочисленный). Просто проглатывать NPE - это плохой стиль, и он просто скрывает существующие ошибки программирования.
Юрген Штробель
7

Исключения NullPointerException обычно являются признаком пропущенной нулевой проверки. Таким образом, вместо того, чтобы ловить это так, вы должны добавить соответствующую нулевую проверку, чтобы не вызывать исключения.

Но иногда целесообразно обрабатывать исключения RunTimeException. Например, когда вы не можете изменить код, чтобы добавить нулевую проверку в соответствующем месте, или когда исключение является чем-то отличным от NullPointerException.

Ваш пример обработки исключений ужасен. При этом вы потеряете трассировку стека и точную информацию о проблеме. И вы на самом деле не решаете эту проблему, так как вы, вероятно, вызовете еще одно исключение NullPointerException в другом месте и получите вводящую в заблуждение информацию о том, что произошло и как это решить.

deadalnix
источник
1
Я хотел бы добавить, что нулевая проверка - лучшее решение с точки зрения производительности.
Махмуд Хоссам
... тем не менее, если вы делаете десятки распределений, которые «никогда не должны сбоить» в одном месте, добавление нулевых проверок для каждого из них может смешно запутывать код. return null;Лучшим решением будет всеобъемлющее исключение (которое будет элегантно, а не просто ).
SF.
Вы, вероятно, путаете Java с чем-то другим (C ++ для примера). Когда распределение завершается неудачно, вы получаете OutOfMemoryError или что-то подобное, а не нулевой указатель. Скрытие нулевого возврата вместо исключения означает скрытие ошибки, ожидающей взрыва кода в другом месте.
Deadalnix
newбросает std::bad_allocв C ++.
Р. Мартиньо Фернандес
Предполагая, что новый оператор не перегружен, что является обычной практикой. В C ++ может произойти все что угодно;) Но хорошо, допустим, malloc в C. Важным моментом было то, что вы никогда не получили это в Java.
Deadalnix
4

Я работаю с ожидаемыми исключениями там, где я их ожидаю. (Как ошибки чтения / записи БД). Неожиданные исключения я всплываю. Где-то еще можно ожидать исключения и иметь логику для этого.

SoylentGray
источник
2

Исключения должны быть именно такими ... исключениями. При использовании исключений рекомендуется использовать их для освещения ситуации, в которой происходит нечто, противоречащее тому, что вы ожидаете. Классическим примером является исключение FileNotFoundException, которое выдается, когда файла просто нет. Если вы проверяете существование файла, вы используете File.exists (), так как вы просто толкаете 10-футовую палку, чтобы увидеть, ударили ли вы что-нибудь.

Технически вы можете достичь тех же результатов, окружив его попыткой catch и используя файл, как если бы он существовал, но A) исключения, как правило, являются дорогостоящими, и B) программисты предполагают, что вы подразумевали, что файл существует, если он был в попытке поймать, что добавляет общую путаницу программы.

Во многих ситуациях я напишу метод, который выбирает какое-то значение из базы данных. Тысяча вещей может пойти не так, и, учитывая, что мне нужен только один маленький фрагмент информации, неудобно окружать вызов списком перехвата попыток, который содержит 5 различных исключений. Итак, я поймаю исключения в методе fetch. Если что-то пойдет не так, я предприму все необходимые действия, чтобы закрыть соединение с базой данных или еще что-то в предложении finally и вернуть ноль. Это хорошая практика не только потому, что это упрощает ваш код, но также и потому, что «null» отправляет то же сообщение, которое вы могли получить из исключения… что-то пошло не так, как планировалось. Управляйте особенностями исключений в методе fetch, но управляйте тем, что делать, когда что-то не так

Например:

Integer getUserCount() {
   Integer result = null;
   try {
      // Attempt to open database and retrieve data
   } catch (TimeoutException e) {
      logger.error("Got a watch?");
   } catch (MissingDatabaseException e) {
      logger.error("What are you smoking?");
   } catch (PermissionsToReadException e) {
      logger.error("Did you *really* think you were getting away with that?");
   } catch (PressedSendButtonToHardException e) {
      logger.error("Seriously.. just back away from the computer... slowly..");
   } catch (WTFException e) {
      logger.error("You're on your own with this one.. I don't even know what happened..");
   } finally {
      // Close connections and whatnot
   }
   return result;
}

void doStuff() {
   Integer result = getUserCount();
   if(result != null) {
       // Went as planned..
   }
}
Нил
источник
6
Если вы используете File.exists () и полагаетесь на результат, может возникнуть условие гонки, если файл будет удален прямо между File.exists () и File.open (). Если это вызывает критическую ошибку безопасности, злоумышленник может вызвать это условие специально. Из-за этого иногда лучше сохранять операцию атомарной, то есть попробовать ее и поймать исключение.
user281377
1
Кроме того, существуют файлы, которые должны существовать для запуска приложения. Это были бы исключительные условия. Если для работы приложения необходим файл, то нет причин не читать его, а затем обрабатывать исключение для исключительного случая. Я считаю, что это делает намерение более ясным.
Томас Оуэнс
Это плохое решение вернуть NULL. Это приведет к NullPointerException в какой-то момент и будет действительно трудно отладить то, что пошло не так. Потому что кто-то в какой-то момент забудет нулевую проверку.
Deadalnix
@deadalnix: Я мог бы поспорить, что вы могли бы так же легко забыть окружить попыткой поймать иначе. Разница - это стиль, а не функциональность.
Нил
@ammoQ: я не согласен. Я думаю, что вы должны использовать File.exists (), и в редких случаях, когда он удаляется перед использованием, исключение более чем уместно. Разница в том, где вы держите свой улов. Просто запишите общий список исключений для непредвиденных ошибок, чтобы зарегистрировать и сообщить об этом.
Нил
-5

да, вы правильно обрабатываете исключения во время выполнения, что не является хорошей практикой.

prasonscala
источник
5
Привет prassee, не могли бы вы уточнить свой ответ? Однострочники, которые не подкреплены фактами, ссылками или опытом, не очень полезны.