Что делает ключевое слово Java assert и когда его следует использовать?

602

На каких реальных примерах можно понять ключевую роль утверждений?

Praveen
источник
8
В реальной жизни вы их почти не видите. Гипотеза: если вы используете утверждения, вам нужно подумать о трех состояниях: утверждение проходит, утверждение не выполняется, утверждение отключено, а не только два. И assert отключен по умолчанию, так что это наиболее вероятное состояние, и трудно гарантировать, что он включен для вашего кода. Это означает, что утверждения являются преждевременной оптимизацией, которая будет иметь ограниченное применение. Как вы видите в ответе @ Bjorn, даже трудно придумать вариант использования, при котором вы не захотите все время проваливать утверждение.
Ишай
35
@ Yishai: «Вы должны думать о ... Утверждение выключено» Если вам нужно сделать это, вы делаете это неправильно. «Утверждения - это преждевременная оптимизация ограниченного использования». Это в значительной степени не по пути. Вот что говорит Sun по этому поводу: « Использование утверждений в технологии Java », и это также полезно прочитать: « Преимущества программирования с утверждениями (или утверждениями assert) »
Дэвид Тонхофер,
5
@DavidTonhofer, в реальной жизни вы их почти не видите. Это поддается проверке. Отметьте столько проектов с открытым исходным кодом, сколько захотите. Я не говорю, что вы не проверяете инварианты. Это не одно и то же. Перефразируй. Если утверждения так важны, почему они отключены по умолчанию?
Ишай
17
Ссылка, FWIW: связь между утверждениями программного обеспечения и качеством кода : «Мы также сравниваем эффективность утверждений с эффективностью популярных методов поиска ошибок, таких как инструменты статического анализа исходного кода. Мы наблюдаем из нашего примера, что с увеличением плотности утверждений в файле есть статистически значимое уменьшение плотности ошибок ".
Дэвид Тонхофер
4
@DavidTonhofer Дэвид, я думаю, что ваша любовь к утверждению относится к очень специфическому типу программирования, которое вы, ребята, делаете, в моей области, которая работает с веб-приложениями, выход из программы по ЛЮБОЙ причине - самая большая НЕТ НЕТ - лично я никогда используется утверждение, отличное от модульного / целочисленного тестирования
ночной граф

Ответы:

426

Утверждения (посредством ключевого слова assert ) были добавлены в Java 1.4. Они используются для проверки правильности инварианта в коде. Они никогда не должны запускаться в рабочем коде и указывают на ошибку или неправильное использование пути кода. Их можно активировать во время выполнения с помощью -eaпараметра javaкоманды, но по умолчанию они не включены.

Пример:

public Foo acquireFoo(int id) {
  Foo result = null;
  if (id > 50) {
    result = fooService.read(id);
  } else {
    result = new Foo(id);
  }
  assert result != null;

  return result;
}
змееподобный
источник
71
Фактически, Oracle говорит вам не использовать его assertдля проверки параметров открытого метода ( docs.oracle.com/javase/1.4.2/docs/guide/lang/assert.html ). Это должно бросить Exceptionвместо того, чтобы убить программу.
SJuan76
10
Но вы все еще не объясняете, почему они существуют. Почему вы не можете выполнить проверку if () и вызвать исключение?
Эль Мак
7
@ElMac - утверждения для dev / debug / test частей цикла - они не для производства. Блок if работает в prod. Простые утверждения не сломают банк, но дорогие утверждения, которые выполняют сложную проверку данных, могут разрушить вашу производственную среду, поэтому они там отключены.
hoodaticus
2
@hoodaticus вы имеете в виду только тот факт, что я могу включить / выключить все утверждения для кода продукта является причиной? Потому что я могу в любом случае выполнить сложную проверку данных, а затем обработать ее с помощью исключений. Если у меня есть рабочий код, я мог бы отключить сложные (и, возможно, дорогие) утверждения, потому что он должен работать и уже был протестирован? Теоретически они не должны закрывать программу, потому что тогда у вас все равно будут проблемы.
El Mac
8
This convention is unaffected by the addition of the assert construct. Do not use assertions to check the parameters of a public method. An assert is inappropriate because the method guarantees that it will always enforce the argument checks. It must check its arguments whether or not assertions are enabled. Further, the assert construct does not throw an exception of the specified type. It can throw only an AssertionError. docs.oracle.com/javase/8/docs/technotes/guides/language/…
Бахши
325

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

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

Эта идея основана на парадигме Design-by-Contract (DbC): вы сначала определяете (с математической точностью), что должен делать ваш метод, а затем проверяете это, проверяя его во время фактического выполнения. Пример:

// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
  return a + b;
}

Хотя это совершенно очевидно, что работает нормально, большинство программистов не увидят скрытую ошибку внутри этой (подсказка: Ariane V разбился из-за аналогичной ошибки). Теперь DbC определяет, что вы всегда должны проверять ввод и вывод функции, чтобы убедиться, что она работает правильно. Java может сделать это с помощью утверждений:

// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
    assert (Integer.MAX_VALUE - a >= b) : "Value of " + a + " + " + b + " is too large to add.";
  final int result = a + b;
    assert (result - a == b) : "Sum of " + a + " + " + b + " returned wrong sum " + result;
  return result;
}

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

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

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

TwoThe
источник
29
Я выбрал этот пример, потому что он очень хорошо представляет скрытые ошибки в, казалось бы, безошибочном коде. Если это похоже на то, что представил кто-то другой, то, возможно, они имели в виду ту же идею. ;)
Два
8
Вы выбираете assert, потому что он не выполняется, когда утверждение ложно. Если может иметь любое поведение. Удар по крайним случаям - работа модульного тестирования. Использование Design by Contract довольно точно определило контракт, но, как и в случае реальных контрактов, вам нужен контроль, чтобы убедиться, что они соблюдаются. С утверждениями вставлен сторожевой таймер, который будет вам тогда, когда контракт не будет соблюдаться. Думайте об этом как о ныряющем адвокате, кричащем «НЕПРАВИЛЬНО» каждый раз, когда вы делаете что-то за пределами или против подписанного вами контракта, а затем отправляете вас домой, чтобы вы не могли продолжать работать и дальше нарушать договор!
Эрик
5
Необходим в этом простом случае: нет, но DbC определяет, что каждый результат должен быть проверен. Представьте, что кто-то теперь изменяет эту функцию на что-то гораздо более сложное, затем он должен также адаптировать пост-проверку, и тогда она внезапно становится полезной.
Два
4
Извините, что воскресил это, но у меня есть конкретный вопрос. В чем разница между тем, что сделал @TwoThe, и вместо использования assert, просто добавив new IllegalArgumentExceptionсообщение? Я имею в виду, помимо добавления o throwsк объявлению метода и коду для управления этим исключением где-то еще. Зачем assertвставлять новые исключения? Или почему не ifвместо assert? Не могу получить это :(
Blueriver
14
-1: утверждение для проверки на переполнение неверно, если aможет быть отрицательным. Второе утверждение бесполезно; для значений int всегда есть случай, когда a + b - b == a. Этот тест может провалиться только в случае поломки компьютера. Чтобы защититься от этой непредвиденной ситуации, вам необходимо проверить согласованность между несколькими процессорами.
Кевин Клайн
63

Утверждения - это инструмент фазы разработки для выявления ошибок в вашем коде. Они разработаны так, чтобы их можно было легко удалить, поэтому они не будут существовать в рабочем коде. Таким образом, утверждения не являются частью «решения», которое вы предоставляете клиенту. Это внутренние проверки, чтобы убедиться, что ваши предположения верны. Наиболее распространенным примером является проверка на ноль. Многие методы написаны так:

void doSomething(Widget widget) {
  if (widget != null) {
    widget.someMethod(); // ...
    ... // do more stuff with this widget
  }
}

Очень часто в таком методе виджет никогда не должен быть нулевым. Так что, если он нулевой, в вашем коде есть ошибка, которую нужно отследить. Но код выше никогда не скажет вам этого. Поэтому, стараясь написать «безопасный» код, вы также скрываете ошибку. Гораздо лучше написать такой код:

/**
 * @param Widget widget Should never be null
 */
void doSomething(Widget widget) {
  assert widget != null;
  widget.someMethod(); // ...
    ... // do more stuff with this widget
}

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

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

void doSomething(Widget widget) {
  assert widget != null;
  if (widget != null) {
    widget.someMethod(); // ...
    ... // do more stuff with this widget
  }
}

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

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

/**
 * Compare two values using equals(), after checking for null.
 * @param thisValue (may be null)
 * @param otherValue (may be null)
 * @return True if they are both null or if equals() returns true
 */
public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
  } else {
    result = thisValue.equals(otherValue);
  }
  return result;
}

Этот код делегирует работу equals()метода в случае, когда thisValue не является нулевым. Но предполагается, что equals()метод правильно выполняет контракт equals(), правильно обрабатывая нулевой параметр.

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

public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
  } else {
    result = otherValue != null && thisValue.equals(otherValue); // questionable null check
  }
  return result;
}

Дополнительная проверка здесь other != nullнеобходима только в том случае, если equals()метод не может проверить нулевое значение, как того требует его контракт.

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

public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
    assert otherValue == null || otherValue.equals(null) == false;
  } else {
    result = otherValue != null && thisValue.equals(otherValue);
    assert thisValue.equals(null) == false;
  }
  return result;
}

Важные моменты, которые следует иметь в виду:

  1. Утверждения являются только инструментами фазы разработки.

  2. Смысл утверждения в том, чтобы вы знали, есть ли ошибка не только в вашем коде, но и в вашей кодовой базе . (Утверждения здесь будут фактически отмечать ошибки в других классах.)

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

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

  5. Утверждения не изменяют поведение кода в производственной среде, поэтому мой коллега рад, что проверка на ноль есть, и что этот метод будет выполняться правильно, даже если equals()метод содержит ошибки. Я счастлив, потому что я поймаю любой ошибочный equals()метод в разработке.

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

MiguelMunoz
источник
Хорошие замечания о «сокрытии ошибки» и о том, как утверждения выявляют ошибки во время разработки!
Нобар
Ни одна из этих проверок не является медленной, поэтому нет причин отключать их в процессе производства. Они должны быть преобразованы в операторы журналирования, чтобы вы могли обнаружить проблемы, которые не обнаруживаются на вашем «этапе разработки». (Действительно, во всяком случае, такой фазы развития не существует. Разработка заканчивается, когда вы решаете вообще не поддерживать свой код.)
Александр Дубинский
20

Много хороших ответов, объясняющих, что assertделает ключевое слово, но мало отвечающих на реальный вопрос, «когда assertключевое слово следует использовать в реальной жизни?»

Ответ: почти никогда .

Утверждения, как концепция, прекрасны. Хороший код имеет много if (...) throw ...утверждений (и их родственники любят Objects.requireNonNullи Math.addExact). Однако некоторые дизайнерские решения значительно ограничивают полезность самого assert ключевого слова .

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

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

Следовательно, использование if (...) throw ...должно быть предпочтительным, так же как это требуется для проверки значений параметров открытых методов и для бросания IllegalArgumentException.

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

Не используйте assertпросто потому, что он чище и красивее чем if (...) throw ...(и я говорю это с большой болью, потому что мне нравится чистота и красота). Если вы просто не можете себе помочь и можете контролировать запуск своего приложения, тогда не стесняйтесь использовать, assertно всегда включайте утверждения в работе. Правда, это то, что я склонен делать. Я настаиваю на аннотации lombok, которая заставит assertдействовать больше как if (...) throw .... Проголосуйте за это здесь.

(Rant: разработчики JVM были группой ужасных, преждевременно оптимизирующих программистов. Вот почему вы слышали о столь многих проблемах безопасности в плагине Java и JVM. Они отказались включать базовые проверки и утверждения в производственный код, и мы продолжаем заплати цену.)

Александр Дубинский
источник
2
@aberglas Всеобщее предложение есть catch (Throwable t). Нет причин не пытаться перехватывать, регистрировать или повторять попытки / восстанавливаться из OutOfMemoryError, AssertionError и т. Д.
Александр Дубинский,
1
Я поймал и оправился от OutOfMemoryError.
MiguelMunoz
1
Я не согласна Многие из моих утверждений используются, чтобы убедиться, что мой API вызывается правильно. Например, я мог бы написать закрытый метод, который должен вызываться только тогда, когда объект удерживает блокировку. Если другой разработчик вызывает этот метод из части кода, которая не блокирует объект, утверждение сразу скажет им, что они допустили ошибку. Существует много таких ошибок, которые могут, с уверенностью, быть пойманными на этапе разработки, и утверждения очень полезны в этих случаях.
MiguelMunoz
2
@MiguelMunoz В своем ответе я сказал, что идея утверждений очень хорошая. Это реализация assertключевого слова плохо. Я отредактирую свой ответ, чтобы было более понятно, что я имею в виду ключевое слово, а не концепцию.
Александр Дубинский
2
Мне нравится тот факт, что он генерирует AssertionError вместо исключения. Слишком много разработчиков до сих пор не поняли, что они не должны перехватывать Exception, если код может генерировать только что-то вроде IOException. У меня были ошибки в моем коде, которые полностью проглотили, потому что кто-то поймал Exception. Утверждения не попадают в эту ловушку. Исключения составляют ситуации, которые вы ожидаете увидеть в рабочем коде. Что касается ведения журнала, вы должны также регистрировать все свои ошибки, даже если ошибки встречаются редко. Например, вы действительно хотите, чтобы OutOfMemoryError проходил без регистрации?
MiguelMunoz
14

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

switch (fruit) {
  case apple:
    // do something
    break;
  case pear:
    // do something
    break;
  case banana:
    // do something
    break;
}

Пока вы справляетесь с каждым делом, вы в порядке. Но когда-нибудь кто-нибудь добавит fig в ваше перечисление и забудет добавить его в ваш оператор switch. Это приводит к ошибке, которую сложно поймать, потому что эффекты не будут ощущаться до тех пор, пока вы не оставите оператор switch. Но если вы напишите свой переключатель, как это, вы можете сразу же поймать его:

switch (fruit) {
  case apple:
    // do something
    break;
  case pear:
    // do something
    break;
  case banana:
    // do something
    break;
  default:
    assert false : "Missing enum value: " + fruit;
}
MiguelMunoz
источник
4
Вот почему предупреждения должны быть включены, а предупреждения должны рассматриваться как ошибки. Любой наполовину приличный компилятор способен сказать вам, если только вы позволите ему сказать, что вы пропускаете проверку перечисления, и он сделает это во время компиляции, что несказанно лучше, чем (возможно, однажды) узнать в время выполнения.
Майк Накис
9
зачем здесь использовать утверждение, а не какое-то исключение, например, недопустимое исключение argumentex?
liltitus27
4
Это бросит, AssertionErrorесли утверждения включены ( -ea). Каково желаемое поведение в производстве? Тихая неоперация и потенциальная катастрофа позже в исполнении? Возможно нет. Я бы предложил явное throw new AssertionError("Missing enum value: " + fruit);.
aioobe
1
Есть хороший аргумент для того, чтобы просто выдать AssertionError. Что касается правильного поведения на производстве, весь смысл утверждений состоит в том, чтобы не допустить этого на производстве. Утверждения являются инструментом этапа разработки для выявления ошибок, которые можно легко удалить из рабочего кода. В этом случае нет причин удалять его из рабочего кода. Но во многих случаях тесты на целостность могут замедлить процесс. Поместив эти тесты в утверждения, которые не используются в производственном коде, вы можете свободно писать тщательные тесты, не беспокоясь о том, что они замедлят ваш производственный код.
MiguelMunoz
Это кажется неправильным. ИМХО не стоит использовать, defaultчтобы компилятор мог предупредить вас о пропущенных случаях. Вы можете returnвместо этого break(возможно, потребуется извлечь метод), а затем обработать пропущенный случай после переключения. Таким образом, вы получите предупреждение и возможность assert.
Maaartinus
12

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

Примером утверждения может быть проверка того, что определенная группа методов вызывается в правильном порядке (например, который hasNext()вызывается ранее next()в a Iterator).

Donal Fellows
источник
1
Вам не нужно вызывать hasNext () перед next ().
DJClayworth
6
@DJClayworth: Вам не нужно избегать срабатывания утверждений. :-)
Donal Fellows
8

Что делает ключевое слово assert в Java?

Давайте посмотрим на скомпилированный байт-код.

Мы сделаем вывод, что:

public class Assert {
    public static void main(String[] args) {
        assert System.currentTimeMillis() == 0L;
    }
}

генерирует почти тот же байт-код, что и:

public class Assert {
    static final boolean $assertionsDisabled =
        !Assert.class.desiredAssertionStatus();
    public static void main(String[] args) {
        if (!$assertionsDisabled) {
            if (System.currentTimeMillis() != 0L) {
                throw new AssertionError();
            }
        }
    }
}

где Assert.class.desiredAssertionStatus(), trueкогда -eaпередается в командной строке, и ложь в противном случае.

Мы используем, System.currentTimeMillis()чтобы убедиться, что он не будет оптимизирован ( assert true;сделал).

Синтетическое поле генерируется так, что Java нужно вызывать Assert.class.desiredAssertionStatus()только один раз во время загрузки, а затем кэшировать результат там. Смотрите также: Что означает «статический синтетический»?

Мы можем проверить это с помощью:

javac Assert.java
javap -c -constants -private -verbose Assert.class

В Oracle JDK 1.8.0_45 было сгенерировано синтетическое статическое поле (см. Также: Что означает «статический синтез»? ):

static final boolean $assertionsDisabled;
  descriptor: Z
  flags: ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC

вместе со статическим инициализатором:

 0: ldc           #6                  // class Assert
 2: invokevirtual #7                  // Method java/lang Class.desiredAssertionStatus:()Z
 5: ifne          12
 8: iconst_1
 9: goto          13
12: iconst_0
13: putstatic     #2                  // Field $assertionsDisabled:Z
16: return

и основным методом является:

 0: getstatic     #2                  // Field $assertionsDisabled:Z
 3: ifne          22
 6: invokestatic  #3                  // Method java/lang/System.currentTimeMillis:()J
 9: lconst_0
10: lcmp
11: ifeq          22
14: new           #4                  // class java/lang/AssertionError
17: dup
18: invokespecial #5                  // Method java/lang/AssertionError."<init>":()V
21: athrow
22: return

Мы заключаем, что:

  • поддержка уровня байт-кода отсутствует assert: это концепция языка Java
  • assertможет быть достаточно хорошо эмулировано с системными свойствами -Pcom.me.assert=trueдля замены -eaв командной строке, и a throw new AssertionError().
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
источник
2
Таким образом, catch (Throwable t)пункт может также отследить нарушения утверждений? Для меня это ограничивает их полезность только тем случаем, когда тело утверждения занимает много времени, что редко.
Евгений Сергеев
1
Я не уверен, почему это ограничивает полезность утверждения. Вы никогда не должны ловить Throwable, за исключением очень редких случаев. Если вам нужно перехватить Throwable, но вы хотите, чтобы он не перехватывал утверждения, вы можете просто перехватить AssertionErrorпервое и перебросить его.
MiguelMunoz
7

Пример из реального мира из класса Stack (из утверждения в статьях Java )

public int pop() {
   // precondition
   assert !isEmpty() : "Stack is empty";
   return stack[--num];
}
Бьерн
источник
80
Это было бы неодобрительно в C: Утверждение - это то, что действительно никогда не должно происходить - выталкивание пустого стека должно вызвать исключение NoElementsException или что-то подобное. Смотрите ответ Донала.
Конерак
4
Согласен. Хотя это взято из официального руководства, это плохой пример.
DJClayworth
4
Это должно выбросить docs.oracle.com/javase/6/docs/api/java/util/…, как в docs.oracle.com/javase/6/docs/api/java/util/…
Арам Кочарян
7
Там, вероятно, утечка памяти. Вы должны установить stack [num] = null; для того, чтобы GC сделал свою работу правильно.
H.Rabiee
3
Я думаю, что в частном методе было бы правильно использовать утверждение, так как было бы странно иметь исключения для сбоя класса или метода. В публичном методе, вызывающем его откуда-то извне, вы не можете точно сказать, как его использует другой код. Это действительно проверяет isEmpty () или нет? Вы не знаете
Власек
7

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

Зачем утверждать что-то, когда вы знаете, что это правда? Это правда только тогда, когда все работает правильно. Если программа имеет дефект, это может быть на самом деле не так. Обнаружение этого ранее в процессе позволяет понять, что что-то не так.

assertЗаявление содержит это заявление вместе с дополнительным Stringсообщением.

Синтаксис для оператора assert имеет две формы:

assert boolean_expression;
assert boolean_expression: error_message;

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

  1. Проверка входных параметров частного метода. НЕ для публичных методов. publicметоды должны генерировать регулярные исключения при передаче неверных параметров.

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

Например, если вы уверены, что это будет только 1 или 2, вы можете использовать утверждение вроде этого:

...
if (i == 1)    {
    ...
}
else if (i == 2)    {
    ...
} else {
    assert false : "cannot happen. i is " + i;
}
...
  1. Проверка почтовых условий в конце любого метода. Это означает, что после выполнения бизнес-логики вы можете использовать утверждения, чтобы гарантировать, что внутреннее состояние ваших переменных или результатов соответствует ожидаемому. Например, метод, который открывает сокет или файл, может использовать утверждение в конце, чтобы убедиться, что сокет или файл действительно открыт.

Утверждения не должны использоваться для:

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

  2. Проверка ограничений на то, что вводится пользователем. То же, что и выше.

  3. Не следует использовать для побочных эффектов.

Например, это неправильное использование, потому что здесь утверждение используется для побочного эффекта вызова doSomething()метода.

public boolean doSomething() {
...    
}
public void someMethod() {       
assert doSomething(); 
}

Единственный случай, когда это может быть оправдано, - это когда вы пытаетесь выяснить, включены ли утверждения в вашем коде:   

boolean enabled = false;    
assert enabled = true;    
if (enabled) {
    System.out.println("Assertions are enabled");
} else {
    System.out.println("Assertions are disabled");
}
solomkinmv
источник
5

В дополнение ко всем отличным ответам, представленным здесь, официальное руководство по программированию на Java SE 7 содержит довольно краткое руководство по использованию assert; с несколькими точными примерами того, когда хорошая (и, что важно, плохая) идея использовать утверждения, и чем она отличается от создания исключений.

Ссылка на сайт

Иван Барцов
источник
1
Согласен. В статье много прекрасных примеров. Мне особенно понравился тот, который гарантирует, что метод вызывается только тогда, когда объект удерживает блокировку.
MiguelMunoz
4

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

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

Я люблю это, но не знаю, как включить его в Eclipse / Android / ADT. Кажется, он выключен даже при отладке. (На этом есть поток, но он ссылается на «Java vm», который не отображается в конфигурации запуска ADT).

Джон Уайт
источник
1
Чтобы включить утверждение в IDE затмения, пожалуйста, следуйте tutoringcenter.cs.usfca.edu/resources/…
Аяз Паша,
Я не думаю, что есть способ включить утверждения в Android. Это очень разочаровывает.
MiguelMunoz
3

Вот утверждение, которое я написал на сервере для проекта Hibernate / SQL. Компонент-сущность имеет два фактически булевых свойства: isActive и isDefault. Каждый из них может иметь значение «Y» или «N» или ноль, что рассматривается как «N». Мы хотим убедиться, что клиент браузера ограничен этими тремя значениями. Итак, в мои сеттеры для этих двух свойств я добавил это утверждение:

assert new HashSet<String>(Arrays.asList("Y", "N", null)).contains(value) : value;

Обратите внимание на следующее.

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

  2. Это утверждение медленное и неэффективное. Это нормально. Утверждения могут быть медленными. Нам все равно, потому что это инструменты только для разработки. Это не замедлит рабочий код, потому что утверждения будут отключены. (Есть некоторые разногласия по этому вопросу, к которым я вернусь позже.) Это приводит к моей следующей точке.

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

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

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

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

Кстати, я мог бы написать это так:

assert value == null || value.equals("Y") || value.equals("N") : value;

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

MiguelMunoz
источник
Я сильно сомневаюсь, что использование такого маленького a HashSetдает какое-либо преимущество в скорости перед ArrayList. Кроме того, набор и список создания доминируют время поиска. Они будут в порядке, используя константу. Это все сказано +1.
Мааартин
Все верно. Я сделал это таким неэффективным способом, чтобы проиллюстрировать мою точку зрения о том, что утверждения могут быть медленными. Это можно сделать более эффективным, но есть другие, которые не могут. В превосходной книге под названием «Написание твердого кода» Стив Магуайр рассказывает об утверждении в Microsoft Excel для тестирования нового кода инкрементного обновления, пропускающего ячейки, которые не должны изменяться. Каждый раз, когда пользователь вносил изменения, утверждение пересчитывало всю электронную таблицу, чтобы убедиться, что результаты совпадают с результатами функции добавочного обновления. Это действительно замедлило отладочную версию, но они рано обнаружили все свои ошибки.
MiguelMunoz
Полностью согласен Утверждения - это своего рода тесты - они менее универсальны, чем обычные тесты, но они могут охватывать частные методы и писать их намного дешевле. Я постараюсь использовать их еще больше.
Maaartinus
2

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

Утверждение работает во время выполнения. Простой пример, который может очень просто объяснить всю концепцию, приведен ниже. Что делает ключевое слово assert в Java? (WikiAnswers).

SBTec
источник
2

Утверждения по умолчанию отключены. Чтобы включить их, мы должны запустить программу с -eaпараметрами (степень детализации можно варьировать). Например, java -ea AssertionsDemo.

Существует два формата использования утверждений:

  1. Просто: например. assert 1==2; // This will raise an AssertionError,
  2. Лучше: assert 1==2: "no way.. 1 is not equal to 2"; это вызовет ошибку AssertionError с отображаемым сообщением, и, следовательно, будет лучше. Хотя фактический синтаксис - это assert expr1:expr2когда expr2 может быть любым выражением, возвращающим значение, я использовал его чаще, просто чтобы напечатать сообщение.
Чандан Пурохит
источник
1

Напомним (и это верно для многих языков, не только Java):

«assert» в основном используется в качестве средства отладки разработчиками программного обеспечения во время процесса отладки. Assert-сообщения никогда не должны появляться. Многие языки предоставляют опцию времени компиляции, которая заставит игнорировать все «утверждения» для использования при генерации «производственного» кода.

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

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

Исключения обычно позволяют условиям фатальной ошибки быть такими, как «исключения из правила». И для них должен обрабатываться кодовый путь, который также является «исключением из правила ... » fly ball! »

Майк Робинсон
источник
1

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

  • Их нельзя использовать для проверки аргументов открытых методов, поскольку вы не можете их контролировать.
  • Их не следует использовать для простых проверок, например, result != nullтакие проверки очень быстрые, и вряд ли что-то можно сохранить.

Итак, что осталось? Дорогие чеки на условия действительно ожидаемые, чтобы быть правдой. Хорошим примером будут инварианты структуры данных, такой как RB-дерево. На самом деле, в ConcurrentHashMapJDK8 есть несколько таких значимых утверждений для TreeNodes.

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

Иногда чек не очень дорогой, но в то же время вы уверены, что он пройдет. В моем коде, например,

assert Sets.newHashSet(userIds).size() == userIds.size();

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

maaartinus
источник
0

В основном, «assert true» пройдет, а «assert false» потерпит неудачу. Давайте посмотрим, как это будет работать:

public static void main(String[] args)
{
    String s1 = "Hello";
    assert checkInteger(s1);
}

private static boolean checkInteger(String s)
{
    try {
        Integer.parseInt(s);
        return true;
    }
    catch(Exception e)
    {
        return false;
    }
}
Питер Мортенсен
источник
-8

assertэто ключевое слово. Он был введен в JDK 1.4. Есть два типа assertс

  1. Очень простые assertутверждения
  2. Простые assertзаявления.

По умолчанию все assertоператоры не будут выполнены. Если assertоператор получает false, он автоматически выдаст ошибку утверждения.

pavani
источник
1
Это не дает никакого реального примера из жизни, который является целью вопроса
rubenafo
1
Вы только что скопировали вставку с: amazon.com/Programmer-Study-1Z0-803-1Z0-804-Certification/dp/… ?
Корай Тугай