Когда использовать утверждение, а когда использовать исключение

Большую часть времени я буду использовать исключение для проверки условия в моем коде. Интересно, когда подходящее время использовать утверждение?

Например,

Group group=null;
try{
    group = service().getGroup("abc");
}catch(Exception e){
    //I dont log error because I know whenever error occur mean group not found
}

if(group !=null)
{
    //do something
}

Не могли бы вы указать, как утверждение вписывается здесь? Должен ли я использовать утверждение?

Кажется, я никогда не использую утверждения в производственном коде и вижу только утверждения в модульных тестах. Я знаю, что в большинстве случаев я могу просто использовать исключение для проверки, как описано выше, но я хочу знать соответствующий способ сделать это "профессионально".

12 ответов

Решение

Утверждения должны использоваться для проверки того, что никогда не должно происходить, в то время как исключение должно использоваться для проверки того, что может произойти.

Например, функция может делиться на 0, поэтому следует использовать исключение, но можно использовать утверждение для проверки того, что жесткий диск внезапно исчезает.

Утверждение остановит выполнение программы, но исключение позволит программе продолжить работу.

Обратите внимание, что if(group != null) это не утверждение, это просто условно.

Не в своем уме (список может быть неполным и слишком длинным, чтобы помещаться в комментарии), я бы сказал:

  • использовать исключения при проверке параметров, передаваемых в открытые или защищенные методы и конструкторы
  • использовать исключения при взаимодействии с пользователем или когда вы ожидаете, что клиентский код восстановится из исключительной ситуации
  • использовать исключения для решения проблем, которые могут возникнуть
  • использовать утверждения при проверке предварительных условий, постусловий и инвариантов частного / внутреннего кода
  • используйте утверждения, чтобы оставить отзыв себе или своей команде разработчиков
  • используйте утверждения при проверке вещей, которые вряд ли произойдут, иначе это означает, что в вашем приложении есть серьезная ошибка
  • используйте утверждения, чтобы утверждать, что вы (предположительно) знаете, что это правда

Другими словами, исключения касаются надежности вашего приложения, а утверждения - его правильности.

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

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

Также вам следует прочитать статью Oracle об assert, чтобы увидеть больше случаев, когда использовать - или не использовать - assert.

Как общее правило:

  • Используйте утверждения для внутренних проверок согласованности, когда вообще не имеет значения, если кто-то их отключает. (Обратите внимание, что java команда отключает все утверждения по умолчанию.)
  • Используйте регулярные тесты для любых проверок, которые не следует отключать. Это включает в себя защитные проверки, которые защищают от потенциального ущерба, вызванного ошибками, и любые данные / запросы проверки / что-либо, предоставленное пользователями или внешними службами.

Следующий код из вашего вопроса плохой стиль и потенциально глючит

try {
    group = service().getGroup("abc");
} catch (Exception e) {
    //i dont log error because i know whenever error occur mean group not found
}

Проблема в том, что вы НЕ знаете, что исключение означает, что группа не была найдена. Также возможно, что service() вызов сгенерировал исключение или что он вернулся null который затем вызвал NullPointerException,

Когда вы ловите "ожидаемое" исключение, вы должны ловить только то, что ожидаете. Ловя java.lang.Exception (и, особенно, не регистрируя его), вы усложняете диагностику / отладку проблемы и потенциально позволяете приложению наносить больший ущерб.

Итак, вернувшись в Microsoft, мы рекомендовали создавать исключения во всех API-интерфейсах, которые вы делаете общедоступными, и использовать Asserts во всех возможных предположениях относительно внутреннего кода. Это довольно слабое определение, но я думаю, что каждый разработчик должен провести черту.

Что касается использования Исключений, как следует из названия, их использование должно быть исключительным, поэтому для кода, представленного выше, getGroup звонок должен вернуться null если сервис не существует Исключение должно происходить только в том случае, если сетевое соединение отключается или что-то в этом роде.

Я предполагаю, что вывод состоит в том, что команде разработчиков по каждому приложению нужно немного определить границы утверждений и исключений.

В соответствии с этим документом http://docs.oracle.com/javase/6/docs/technotes/guides/language/assert.html, "оператор assert подходит для закрытых предварительных условий, постусловий и инвариантов классов Проверка. Открытая проверка предварительных условий должна по-прежнему выполняться с помощью проверок внутри методов, которые приводят, в частности, к документированным исключениям, таким как IllegalArgumentException и IllegalStateException."

Если вы хотите узнать больше о предусловиях, постусловиях и инвариантах классов, проверьте этот документ: http://docs.oracle.com/javase/6/docs/technotes/guides/language/assert.html. Он также содержит примеры использования утверждений.

См. Раздел 6.1.2 (Утверждения и другие коды ошибок) документации Sun по следующей ссылке.

http://www.oracle.com/technetwork/articles/javase/javapch06.pdf

Этот документ дает лучший совет, который я видел, когда использовать утверждения. Цитата из документа:

"Хорошее эмпирическое правило заключается в том, что вы должны использовать утверждение для исключительных случаев, о которых вы хотели бы забыть. Утверждение - это самый быстрый способ справиться с ситуацией или состоянием, о которых вы не ожидаете, и забыть о них. иметь дело с."

Тестирование на ноль будет ловить только нули, вызывающие проблемы, тогда как попытка / отлов, как у вас, поймает любую ошибку.

В целом, try/catch безопаснее, но немного медленнее, и вы должны быть осторожны, чтобы уловить все возможные ошибки. Поэтому я бы сказал, что используйте try/catch - однажды код getGroup может измениться, и вам может понадобиться эта большая сеть.

Признаюсь, я немного смущен вашим вопросом. Когда условие утверждения не выполняется, генерируется исключение. Смущает, что это называется AssertionError. Обратите внимание, что он не отмечен, например (например) IllegalArgumentException, который генерируется при очень похожих обстоятельствах.

Таким образом, используя утверждения в Java

  1. является более кратким средством записи блока условия / броска
  2. позволяет включать / выключать эти проверки с помощью параметров JVM. Обычно я оставляю эти проверки постоянно, если только они не влияют на производительность во время выполнения или не имеют аналогичного штрафа.

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

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

У меня нет опыта работы с утверждениями Java, но я думаю, что ответ здесь естественным образом следует из того, что такое утверждения Java :

  1. Код, который запускается, только если включен
  2. Отключено по умолчанию

Таким образом, мы можем предположить, что утверждения никогда не должны использоваться для реализации логики приложения: они должны использоваться только как необязательные (из-за функции включения/выключения) проверки работоспособности/явные предположения. Корректность вашего приложения (по модулю ошибок!) не может зависеть от флага времени выполнения, который по умолчанию отключен.

Поэтому используйте утверждения только для проверки внутренних предположений ( только ваш собственный код , а не вещи, которые находятся вне вашего контроля, такие как пользовательский ввод). Но, с другой стороны, иногда вы можете захотеть использовать исключения для проверки своих предположений; если вы хотите всегда проверять определенное предположение, вам не нужна функция включения/выключения, которую предоставляют утверждения.

Кажется, что для задачи проверки предположений утверждения превосходны, когда:

  1. Проверка может быть дорогостоящей, поэтому вы хотите отключить ее.

  2. Использование if-or-throw было бы слишком большим «шумом», но вы можете понести затраты на одну маленькуюassert <expr>

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

            // NOTE we KNOW that `x` is not null!
    x.doWork();
    

    Тогда говорят, что вы всегда должны заменять эту строку на:

            assert x != null;
    x.doWork();
    

    Или (на мой взгляд), если вы предпочитаете:

            if (x == null) {
        throw new IllegalStateException("shouldn’t be null");
    }
    x.doWork();
    

Вещи, которые следуют из этих принципов

Я думаю, что мы можем сразу же расширить разработчики Javaсвязанные принципы (одноуровневый ответ) , используя этот «необязательный» принцип (мой комментарий курсивом):

  • использовать исключения при проверке параметров, передаваемых в общедоступные или защищенные методы и конструкторы
    • Потому что вы проверяете вещи за пределами вашего кода: код, поступающий в ваш модуль
  • использовать исключения при взаимодействии с пользователем или когда вы ожидаете, что клиентский код восстановится после исключительной ситуации
    • Потому что ввод исходит от пользователя и не имеет ничего общего с вашим кодом. Кроме того, утверждения — это не то, от чего вы должны восстанавливаться.(†1)
  • использовать исключения для решения проблем, которые могут возникнуть
    • Т.е. ошибки, которые происходят само собой: не баги в вашем коде
  • использовать утверждения при проверке предусловий, постусловий и инвариантов частного/внутреннего кода
    • Потому что это ваш собственный код: проблемы внутри вашего собственного кода возникают сами по себе.
  • используйте утверждения, чтобы предоставить обратную связь себе или своей команде разработчиков
    • С ошибками должны разбираться разработчики
  • используйте утверждения при проверке вещей, которые очень маловероятны, иначе это означает, что в вашем приложении есть серьезный недостаток
    • Ключевое слово «серьезный недостаток в вашем приложении»
  • используйте утверждения, чтобы заявить о том, что вы (предположительно) знаете, чтобы быть правдой
    • Проверка предположений

Примечания

  1. Думаю, это общее мнение. См. также (выделено жирным шрифтом):

    Почему AssertionError является подклассом Error, а не RuntimeException?
    Этот вопрос был спорным. Группа экспертов подробно обсудила это и пришла к выводу, что Error больше подходит для того, чтобы отбить у программистов охоту восстанавливаться после неудачных утверждений.

Другие вопросы по тегам