Является ли утверждение Java нарушенным?

Во время обсуждения вопросов я недавно обнаружил ключевое слово assert в Java. Сначала я был в восторге. Что-то полезное я еще не знал! Более эффективный способ проверить правильность входных параметров! Yay learning!

Но потом я посмотрел поближе, и мой энтузиазм был не столько "закаленным", сколько "полностью выдутым" одним простым фактом: вы можете отключить утверждения. *

Это звучит как кошмар. Если я утверждаю, что я не хочу, чтобы код продолжал идти, если вход listOfStuff равен null, почему я бы хотел, чтобы это утверждение игнорировалось? Похоже, если я отлаживаю кусок производственного кода и подозреваю, что listOfStuff, возможно, был ошибочно передан null, но не видит никаких доказательств logfile, вызывающих это утверждение, я не могу доверять, что listOfStuff действительно получил отправленное действительное значение; Я также должен учитывать возможность того, что утверждения могут быть полностью отключены.

И это предполагает, что я один отлаживаю код. Кто-то, кто не знаком с утверждениями, может увидеть это и предположить (вполне разумно), что если сообщение подтверждения не появляется в журнале, listOfStuff не может быть проблемой. Если ваша первая встреча с assert была в дикой природе, вам даже пришло бы в голову, что ее можно полностью отключить? Это не похоже на опцию командной строки, которая позволяет отключить блоки try/catch в конце концов.

Все это подводит меня к моему вопросу (и этот является вопросом, а не оправданием для разглагольства! Я обещаю!):

Что мне не хватает?

Есть ли какой-то нюанс, который делает реализацию Java assert гораздо более полезной, чем я за это благодарю? Является ли возможность включить/отключить его из командной строки, действительно невероятно ценную в некоторых контекстах? Я как-то неправильно понимаю его, когда я предполагаю использовать его в производственном коде вместо утверждений типа if (listOfStuff == null) barf();?

Я просто чувствую, что здесь что-то важное, что я не получаю.

* Хорошо, технически говоря, они фактически отключены по умолчанию; вы должны уйти с дороги, чтобы включить их. Но все же вы можете полностью их выбить.


Изменить: Запрошенное просвещение, полученное просвещение.

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

Я все еще не согласен с тем, что вводные проверки для нетривиальных частных методов должны быть отключены в производственной среде, потому что разработчик считает, что плохие входы невозможны. По моему опыту, зрелый код производства - сумасшедшая, растянутая вещь, разработанная в течение многих лет людьми с разной степенью мастерства, ориентированными на быстро меняющиеся требования разной степени здравомыслия. И даже если плохой ввод действительно невозможен, кусок неряшливого кода обслуживания через шесть месяцев может изменить это. Ссылка gustafc предоставлена ​​ (спасибо!) включает это в качестве примера:

assert interval > 0 && interval <= 1000/MAX_REFRESH_RATE : interval;

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

Кроме того, я могу определенно увидеть значение чего-то вроде этого:

assert reallyExpensiveSanityCheck(someObject) : someObject;

Спасибо всем, кто нашел время, чтобы помочь мне понять эту особенность; это очень ценится.

Ответ 1

assert - полезный фрагмент Дизайн по контракту. В этом контексте утверждения могут быть использованы в:

  • Предварительные условия.
  • Проверки после условия.
  • Промежуточные проверки результатов.
  • Проверка инвариантов класса.

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

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

Ответ 2

Java-утверждения на самом деле не сделаны для проверки аргументов - он специально заявил, что утверждения не должны использоваться вместо старого старого IllegalArgumentException (и не то, как они используются на языках C-ish). Они больше для внутренней проверки, чтобы вы могли сделать предположение о коде, который не является очевидным, если посмотреть на него.

Как для их отключения, вы также делаете это в C (++), только если кто-то получил строчку без утверждений, у них нет возможности включить ее. В Java вы просто перезапустите приложение с соответствующими параметрами VM.

Ответ 3

Я думаю, что его интерпретация и предполагаемое использование интерпретируется и предполагается.

Если вы действительно хотите добавить чек в свой фактический производственный код, почему бы не использовать If напрямую или какой-либо другой условный оператор?

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

Например, проверяя, что объект имеет значение null, пусть разработчик написал частный метод и вызвал его из двух мест (это не идеальный пример, но может работать для частных методов) в классе, где он знает, что он передает не нулевой объект, вместо того, чтобы добавлять ненужную проверку, если с сегодняшнего дня вы знаете, что объект не будет равен нулю Но если кто-то завтра называет этот метод нулевым аргументом, в тестировании модуля разработчика это может быть пойман из-за наличия утверждения и в конечном коде вам все равно не нужна проверка if.

Ответ 4

Каждый язык, который я когда-либо видел с утверждениями, имеет возможность отключить их. Когда вы пишете утверждение, вы должны думать: "Это глупо, во вселенной это не может быть ложным" - если вы считаете, что это может быть ложь, это должна быть проверка ошибок. Утверждение только для того, чтобы помочь вам во время разработки, если что-то идет ужасно неправильно; когда вы создаете код для производства, вы отключите их, чтобы сэкономить время и избежать (надеюсь) лишних проверок.

Ответ 5

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

Что мне не хватает?

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

Идея состоит в том, что если утверждение терпит неудачу, у вас 100% есть ошибка в коде. Утверждения часто используются для идентификации ошибки раньше, чем она могла бы возникнуть иначе.

Ответ 6

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

Например, в C или С++ утверждения отключены в версиях сборки.

Ответ 7

Утверждения - действительно отличный и сжатый инструмент документации для поддерживающего код.

Например, я могу написать:

foo должен быть не нулем и больше чем 0

или поместите это в тело программы:

assert foo != null;
assert foo.value > 0;

Они чрезвычайно ценны для документирования частных/пакетных частных методов для выражения оригинальных инвариантов программиста.

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

Ответ 8

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

Если вы хотите выполнить проверку действительности на входе, вы можете легко написать

if (foobar<=0) throw new BadFoobarException();

или всплывающее окно сообщения или что-то полезное в контексте.

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

Ответ 9

Это не отвечает на ваш вопрос о assert, но я бы рекомендовал проверить класс Preconditions в guava/google-collections. Это позволяет вам писать такие приятные вещи (используя статический импорт):

// 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"); 

Кажется, что-то вроде этого может быть то, что вы хотите (и его нельзя отключить).

Ответ 10

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

Если вы ожидаете, что в производстве произойдет что-то плохое, например listOfStuff, равное нулю, то либо ваш код недостаточно проверен, либо вы не дезактивируете свой ввод, прежде чем позволить вашему коду на нем. В любом случае, "если (плохой материал) {бросить исключение}" было бы лучше. Утверждения предназначены для тестирования/разработки, а не для производства.

Ответ 11

Используйте assert, если вы готовы заплатить $1 конечному пользователю всякий раз, когда утверждение терпит неудачу.

Ошибка утверждения должна быть признаком ошибки проектирования в программе.

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

Утверждение полезно читателям моего кода, так как они видят, что (1) я готов наложить немного денег на это имущество; и (2) в предыдущих исполнениях и тестовых случаях свойство действительно выполнялось.

Моя ставка предполагает, что клиент моего кода придерживается правил и придерживается контракта, с которым мы договорились. Этот контракт может быть толерантным (все входные значения разрешены и проверены на достоверность) или требуются (клиент и я согласились, что он никогда не будет предоставлять определенные входные значения [описанные как предварительные условия] и что он не хочет, чтобы я проверял эти значения снова и снова). Если клиент придерживается правил, и мои утверждения, тем не менее, терпят неудачу, клиент имеет право на некоторую компенсацию.

Ответ 12

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

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

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