Java assert нарушен?
Разыскивая вопросы, я недавно обнаружил assert
Ключевое слово в Java. Сначала я был взволнован. Что-то полезное я еще не знал! Для меня более эффективный способ проверить правильность ввода параметров! Уй учусь!
Но затем я посмотрел поближе, и мой энтузиазм был не столько "смягчен", сколько "полностью подавлен" одним простым фактом: вы можете отключить утверждения.*
Это звучит как кошмар. Если я утверждаю, что я не хочу, чтобы код продолжал работать, если ввод listOfStuff
является null
с какой стати я хочу, чтобы это утверждение игнорировалось? Звучит так, будто я отлаживаю часть производственного кода и подозреваю, что listOfStuff
возможно, был ошибочно передан null
но я не вижу никаких доказательств того, что это утверждение сработало, я не могу доверять этому listOfStuff
фактически получил отправленное действительное значение; Я также должен учитывать возможность того, что утверждения могли быть полностью отключены.
И это предполагает, что я один отлаживаю код. Кто-то незнакомый с утверждениями может увидеть это и предположить (вполне разумно), что если сообщение с подтверждением не появляется в журнале, listOfStuff
не может быть проблемой. Если ваша первая встреча с assert
был в дикой природе, вам даже не пришло в голову, что он может быть полностью отключен? Это не так, как есть опция командной строки, которая позволяет отключить блоки try / catch, в конце концов.
Все это подводит меня к моему вопросу (и это вопрос, а не оправдание для напыщенной речи! Обещаю!):
Что мне не хватает?
Есть ли какой-то нюанс, который делает реализацию Javaassert
куда полезнее, чем я считаю? Действительно ли возможность включить / отключить его из командной строки невероятно ценно в некоторых контекстах? Не понимаю ли я это как-то неправильно, когда я предполагаю использовать его в производственном коде вместо таких утверждений, какif (listOfStuff == null) barf();
?
Я просто чувствую, что здесь есть что-то важное, чего я не понимаю.
* Ладно, технически говоря, по умолчанию они отключены; Вы должны изо всех сил, чтобы включить их. Но, тем не менее, вы можете полностью их выбить.
Изменить: Просветление просили, просветление получено.
Понятие, что assert
Это прежде всего инструмент отладки, который делает его понятным для меня.
Я по-прежнему не согласен с тем, что проверки входных данных для нетривиальных частных методов должны быть отключены в производственной среде, поскольку разработчик считает, что ввод неправильных данных невозможен. По моему опыту, зрелый производственный код - это безумная, растянутая вещь, разрабатываемая годами людьми с разной степенью квалификации, нацеленными на быстро меняющиеся требования разной степени вменяемости. И даже если плохой ввод действительно невозможен, фрагмент небрежного технического обслуживания через шесть месяцев может изменить это. Ссылка, предоставленная gustafc (спасибо!), Включает это в качестве примера:
assert interval > 0 && interval <= 1000/MAX_REFRESH_RATE : interval;
Отключение такой простой проверки в производстве кажется мне глупо оптимистичным. Однако это различие в философии кодирования, а не сломанная особенность.
Кроме того, я определенно вижу ценность чего-то вроде этого:
assert reallyExpensiveSanityCheck(someObject) : someObject;
Спасибо всем, кто нашел время, чтобы помочь мне понять эту функцию; Это очень высоко ценится.
12 ответов
assert
является полезным элементом дизайна по контракту. В этом контексте утверждения могут использоваться в:
- Предварительные проверки.
- Проверка после условия.
- Промежуточный результат проверок.
- Класс инвариантных проверок.
Утверждения могут быть дорогостоящими для оценки (например, инвариант класса, который должен храниться до и после вызова любого открытого метода вашего класса). Утверждения обычно нужны только в отладочных сборках и в целях тестирования; Вы утверждаете вещи, которые не могут произойти - вещи, которые являются синонимом наличия ошибки. Утверждения проверяют ваш код на предмет его собственной семантики.
Утверждения не являются механизмом проверки ввода. Когда ввод может быть действительно правильным или неправильным в производственной среде, то есть для слоев ввода-вывода, используйте другие методы, такие как исключения или старые добрые условные проверки.
Утверждения Java на самом деле не созданы для проверки аргументов - конкретно указано, что утверждения не должны использоваться вместо дорогих старыхIllegalArgumentException
(и это не то, как они используются в языках C-ish). Они предназначены для внутренней проверки, чтобы вы могли сделать предположение о коде, который неочевиден при взгляде на него.
Что касается их отключения, то вы делаете это и в C(++), просто если у кого-то есть сборка без утверждений, у него нет возможности его включить. В Java вы просто перезапускаете приложение с соответствующими параметрами виртуальной машины.
Каждый язык, который я когда-либо видел с утверждениями, имеет возможность отключить их. Когда вы пишете утверждение, вы должны думать, что "это глупо, во вселенной нет никакого способа, чтобы это могло быть ложным" - если вы думаете, что это может быть ложным, это должна быть проверка ошибок. Утверждение просто для того, чтобы помочь вам во время разработки, если что-то пойдет не так; когда вы создаете код для производства, вы отключаете их, чтобы сэкономить время и избежать (надеюсь) лишних проверок
Утверждения предназначены для того, чтобы убедиться, что все, что вы уверены, что ваш код действительно выполняется, выполнено. Это помощь в отладке, на этапе разработки продукта, и обычно не выпускается при выпуске кода.
Что мне не хватает?
Вы не используете утверждения так, как они должны были быть использованы. Вы сказали "проверить правильность входных параметров" - это именно то, что вы не хотите проверять с помощью утверждений.
Идея состоит в том, что если утверждение не выполняется, у вас 100% ошибка в вашем коде. Утверждения часто используются для выявления ошибки раньше, чем она появилась бы в противном случае.
Я думаю, что способ использования утверждений интерпретируется и предвидится.
Если вы действительно хотите добавить чек в свой действующий производственный код, почему бы не использовать If напрямую или любое другое условное выражение?
У тех, кто уже присутствует в языке, идея assert состояла в том, чтобы иметь добавление утверждений разработчика только в том случае, если они действительно не ожидают, что это условие когда-либо произойдет.
Например, проверяя, чтобы объект был нулевым, скажем, разработчик написал закрытый метод и вызвал его из двух мест (это не идеальный пример, но может работать для частных методов) в классе, где он знает, что он передает ненулевой объект вместо добавление ненужной проверки, если, поскольку на сегодняшний день вы знаете, что объект не может быть пустым, но если завтра кто-то вызовет этот метод с нулевым аргументом, в модульном тестировании разработчика это может быть обнаружено из-за наличия утверждения, а в конечном коде вы все еще не используете не нужно, если проверить.
Это звучит примерно так. Утверждения - это всего лишь инструмент, который полезен для отладки кода - их не следует включать постоянно, особенно в рабочем коде.
Например, в C или C++ утверждения отключены в сборках выпуска.
Утверждения - действительно отличный и краткий инструмент документирования для сопровождающего кода.
Например, я могу написать:
foo должно быть ненулевым и больше 0
или поместите это в тело программы:
assert foo != null;
assert foo.value > 0;
Они чрезвычайно полезны для документирования приватных / приватных методов пакета для выражения оригинальных инвариантов программиста.
Для дополнительного бонуса, когда подсистема начинает вести себя неаккуратно, вы можете включить утверждения и мгновенно добавить дополнительную проверку.
Если утверждения нельзя отключить, то почему они вообще должны существовать.
Если вы хотите провести проверку достоверности на входе, вы можете легко написать
if (foobar<=0) throw new BadFoobarException();
или всплывающее окно сообщения или что-то полезное в контексте.
Весь смысл в том, что они могут быть включены для отладки и отключены для производства.
Это не дает прямого ответа на ваш вопрос о assert
, но я бы порекомендовал проверить класс Preconditions в guava/ google-collection. Это позволяет вам писать такие хорошие вещи (используя статический импорт):
// throw NPE if listOfStuff is null
this.listOfStuff = checkNotNull(listOfStuff);
// same, but the NPE will have "listOfStuff" as its message
this.listOfStuff = checkNotNull(listOfStuff, "listOfStuff");
Кажется, что-то вроде этого может быть тем, что вы хотите (и его нельзя отключить).
Утверждения не для конечного пользователя, чтобы увидеть. Они предназначены для программиста, поэтому вы можете убедиться, что код работает правильно во время разработки. После завершения тестирования утверждения обычно отключаются по соображениям производительности.
Если вы ожидаете, что в производстве произойдет что-то плохое, например, listOfStuff будет иметь значение null, то либо ваш код недостаточно протестирован, либо вы не проводите очистку входных данных, прежде чем позволите своему коду это сделать. В любом случае, "если (плохо) {выбросить исключение}" было бы лучше. Утверждения предназначены для времени тестирования / разработки, а не для производства.
Используйте assert
если вы готовы заплатить 1 доллар конечному пользователю в случае неудачного подтверждения.
Ошибка подтверждения должна указывать на ошибку проектирования в программе.
Утверждение утверждает, что я разработал программу таким образом, что я знаю и гарантирую, что указанный предикат всегда выполняется.
Утверждение полезно для читателей моего кода, так как они видят, что (1) я готов установить немного денег на это свойство; и (2) в предыдущих казнях и тестовых случаях свойство действительно сохранялось.
Моя ставка предполагает, что клиент моего кода придерживается правил и придерживается договора, который он и я согласовали. Этот контракт может быть терпимым (все входные значения разрешены и проверены на действительность) или требовательным (клиент и я согласились, что он никогда не предоставит определенные входные значения [описанные как предварительные условия], и что он не хочет, чтобы я проверял эти значения снова и снова). Если клиент придерживается правил, и мои утверждения все же не верны, клиент имеет право на некоторую компенсацию.
Утверждения должны указывать на проблему в коде, которая может быть исправлена, или в качестве помощи при отладке. Вы должны использовать более разрушительный механизм для более серьезных ошибок, таких как остановка программы.
Их также можно использовать для обнаружения неисправимой ошибки до сбоя приложения в дальнейшем в сценариях отладки и тестирования, чтобы помочь вам сузить проблему. Одной из причин этого является то, что проверка целостности не снижает производительность хорошо протестированного кода в рабочей среде.
Кроме того, в некоторых случаях, таких как утечка ресурсов, ситуация может быть нежелательной, но последствия остановки программы хуже, чем последствия продолжения работы.