Що робить ключове слово Java assert і коли його слід використовувати?


601

Наведіть кілька прикладів із реального життя, щоб зрозуміти ключову роль тверджень?


8
У реальному житті їх майже ніколи не бачиш. Концепція: Якщо ви використовуєте твердження, вам доведеться думати про три стани: Асистент проходить, стверджується невдало, затверджується вимкнено, а не лише два. І аргумент вимкнено за замовчуванням, так що це найбільш вірогідний стан, і важко переконатися, що воно включено для вашого коду. Те, що додає, полягає в тому, що твердження - це передчасна оптимізація, яка мала б обмежене використання. Як ви бачите у відповіді @ Bjorn, навіть важко придумати випадок використання, коли ви не хотіли б весь час провалювати ствердження.
Ісіхай

35
@Yishai: "Ви повинні подумати про ... твердження вимкнено" Якщо вам потрібно це зробити, ви робите це неправильно. "твердження - це передчасна оптимізація обмеженого використання". Це в значній мірі поза рейки. Ось, зважаючи на це, Sun: « Використання тверджень у Java-технологіях », і це також добре прочитати: « Переваги програмування з твердженнями (ака твердження тверджень) »
Девід Тонхофер

5
@DavidTonhofer, в реальному житті ти їх майже ніколи не бачиш. Це можна перевірити. Перевірте скільки завгодно проектів з відкритим кодом. Я не кажу, що ви не підтверджуєте інваріантів. Це не те саме. Поставте іншим способом. Якщо твердження такі важливі, чому вони вимкнено за замовчуванням?
Ісіхай

17
Довідка, FWIW: Зв'язок між твердженнями програмного забезпечення та якістю коду : "Ми також порівнюємо ефективність тверджень із популярними методами пошуку помилок, такими як інструменти статичного аналізу вихідного коду. Ми спостерігаємо з нашого прикладу, що зі збільшенням щільності твердження у файлі є статистично значне зниження щільності несправностей. "
Девід Тонхофер

4
@DavidTonhofer David, я думаю, що твоя любов до тверджень полягає в дуже специфічному типі програмування, яке ти робиш, хлопці, в моєму полі, яке працює з веб-додатками, які виходять із програми з будь-якої причини, це найбільший НІ НЕ - я особисто ніколи використаний твердження, окрім одиниці /
цілісного

Відповіді:


426

У Java 1.4 додано твердження (за допомогою ключового слова assrt ). Вони використовуються для перевірки правильності інваріанта в коді. Вони ніколи не повинні спрацьовувати у виробничому коді, вони є вказівкою на помилку чи неправильне використання кодового шляху. Вони можуть бути активовані під час виконання за допомогою -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 () та викинути виняток?
El Mac

7
@ElMac - твердження стосуються частин циклу dev / debug / test - вони не для виробництва. Блок, якщо if працює в prod. Прості твердження не зруйнують банк, але дорогі твердження, які виконують складну перевірку даних, можуть збити ваше виробниче середовище, тому вони вимкнено там.
hoodaticus

2
@hoodaticus Ви маєте на увазі виключно те, що я можу включити / вимкнути всі твердження про код prod? Тому що я можу робити складну перевірку даних у будь-якому випадку, а потім обробляти їх за винятками. Якщо у мене є виробничий код, я можу вимкнути складні (а може і дорогі) твердження, оскільки він повинен працювати і вже був протестований? Теоретично вони не повинні збивати програму, оскільки тоді у вас все одно виникнуть проблеми.
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 не є мовою, яку можна перевірити, це означає: ви не можете підрахувати, що результат вашої операції буде ідеальним. Основною причиною цього є покажчики: вони можуть вказувати куди-небудь або ніде, тому їх не можна обчислити таким саме значенням, принаймні, не в межах розумного діапазону коду. Враховуючи цю проблему, немає ніякого способу довести, що ваш код взагалі правильний. Але те, що ти можеш зробити, це довести, що ти принаймні знаходиш кожну помилку, коли вона трапляється.

Ця ідея заснована на парадигмі « Дизайн за контрактом» (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 - це схоже поняття, але вони не можуть все перевірити. Якщо ви хочете ще більше перевірок (ціною швидкості виконання), вам потрібно використовувати твердження. Це призведе до розмивання вашого коду, але ви, зрештою, зможете доставити продукт на дивно короткий час розробки (чим раніше ви виправите помилку, тим нижча вартість). І крім того: якщо у вашому коді є якась помилка, ви її виявите. Неможливо проскочити помилку і викликати проблеми пізніше.

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


29
Я вибрав цей приклад, оскільки він дуже добре відображає приховані помилки у коді, здавалося б, без помилок. Якщо це схоже на те, що хтось ще представив, то, можливо, вони мали на увазі таку саму ідею. ;)
Дві

8
Ви вибираєте ствердження, оскільки воно не вдається, коли твердження є помилковим. А якщо може мати будь-яку поведінку. Удар по бахромах - це завдання Unit Testing. Використовуючи «Дизайн за контрактом», він досить добре вказав контракт, але, як і для реальних договорів, вам потрібен контроль, щоб переконатися, що вони дотримуються. З твердженнями вставляється сторожовий собака, який буде потім вас, коли контракт буде неповажним. Подумайте про це як про нахабного юриста, який кричить "НЕПРАВИЛЬНО" кожен раз, коли ви робите щось, що знаходиться поза чи проти контракту, який ви підписали, а потім відправляєте додому, щоб ви не могли продовжувати працювати і порушувати договір далі!
Ерік

5
Необхідно в цьому простому випадку: ні, але DbC визначає, що кожен результат повинен бути перевірений. Уявіть, що хтось зараз модифікує цю функцію на щось набагато складніше, тоді він також повинен адаптувати пост-перевірку, і тоді вона раптом стане корисною.
Двоє

4
Вибачте, що воскресив це, але у мене є конкретне питання. Яка різниця між тим, що @TwoThe зробив, і замість того, щоб використовувати assrt, просто кидаючи повідомлення new IllegalArgumentExceptionз повідомленням? Я маю на увазі, окрім того, щоб o додати throwsдо декларації методу та коду, щоб керувати цим винятком десь в іншому місці. Чому assertневдало кидають новий Виняток? Або чому б не ifзамість цього assert? Дійсно цього не вдається отримати :(
Blueriver

14
-1: Твердження про перевірку на переповнення помилкове, якщо воно aможе бути негативним. Друге твердження марне; для значень int завжди буває так, що a + b - b == a. Цей тест може провалитися лише в тому випадку, якщо комп'ютер принципово зламаний. Щоб захиститись від цієї непередбаченої ситуації, вам потрібно перевірити на узгодженість у кількох процесорах.
кевін клайн

63

Твердження - це інструмент на фазі розробки для лову помилок у вашому коді. Вони розроблені для того, щоб їх легко видалити, тому їх не буде існувати у виробничому коді. Тож твердження не є частиною «рішення», яке ви доставляєте замовнику. Вони проводять внутрішні перевірки, щоб переконатися, що припущення, які ви робите, правильні. Найпоширеніший приклад - тестування на null. Багато методів написано так:

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()методу в тому випадку, коли цеValue не є нульовим. Але він передбачає, що 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()метод у розвитку.

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


Хороші моменти щодо "приховування помилки" та того, як стверджує, що виявляють помилки під час розвитку!
nobar

Жодна з цих перевірок не є повільною, тому немає причин вимикати їх у виробництві. Вони повинні бути перетворені в оператори реєстрації, щоб ви могли виявити проблеми, які не з’являються у вашій "фазі розробки". (Дійсно, такого етапу, як етап розробки, немає. Розвиток закінчується, коли ви взагалі вирішите припинити підтримувати свій код.)
Олександр Дубінський

20

Дуже багато хороших відповідей, що пояснюють те, що assertробить ключове слово, але мало хто відповідає на реальне питання, "коли assertключове слово потрібно використовувати в реальному житті?"

Відповідь: майже ніколи .

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

Основна ідея assertключового слова - передчасна оптимізація, і головна особливість полягає в тому, щоб легко відключити всі перевірки. Фактично, assertчеки вимкнено за замовчуванням.

Однак критично важливо, щоб інваріантні перевірки продовжували проводитися у виробництві. Це пояснюється тим, що ідеальне покриття тесту неможливо, і всі виробничі коди містять помилки, твердження яких повинні допомогти діагностувати та зменшувати їх.

Тому використання if (...) throw ...слід віддавати перевагу так само, як це потрібно для перевірки значень параметрів загальнодоступних методів та для метання IllegalArgumentException.

Інколи може виникнути спокуса написати інваріантний чек, який потребує небажаного тривалого часу (і закликається досить часто, щоб він мав значення). Однак такі перевірки уповільнить тестування, що також небажано. Такі трудомісткі перевірки зазвичай записуються як одиничні тести. Тим не менш, іноді може бути доцільним використання assertз цієї причини.

Не використовуйте assertпросто тому, що вона чистіша і красивіша, ніж if (...) throw ...(і я це кажу з великим болем, тому що мені подобається чиста і красива). Якщо ви просто не можете допомогти собі і можете контролювати, як запускати вашу програму, тоді сміливо користуйтесь, assertале завжди включайте твердження у виробництво. Справді, це те, що я прагну робити. Я наполягаю на примітці на ломбок, яка змусить assertдіяти більше схоже if (...) throw .... Голосуйте за це тут.

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


2
@aberglas Застережне положення catch (Throwable t). Немає причин не намагатися захопити, увійти в систему чи повторно спробувати / відновити з OutOfMemoryError, AssertionError тощо.
Олександр Дубінський

1
Я спіймав і відновився з OutOfMemoryError.
MiguelMunoz

1
Я не згоден. Багато моїх тверджень використовуються для того, щоб переконатися, що мій API правильно викликається. Наприклад, я можу написати приватний метод, який слід викликати лише тоді, коли об'єкт містить блокування. Якщо інший розробник викликає цей метод з частини коду, який не блокує об'єкт, твердження відразу скаже їм, що вони допустили помилку. Існує маса помилок на кшталт цієї, які можуть, напевно, потрапити у фазу розвитку, і твердження дуже корисні в цих випадках.
МігельМуноз

2
@MiguelMunoz У своїй відповіді я сказав, що ідея тверджень дуже хороша. Саме реалізація assertключового слова погана. Я відредагую свою відповідь, щоб зрозуміти, що я маю на увазі ключове слово, а не поняття.
Олександр Дубінський

2
Мені подобається те, що він кидає AssertionError замість Винятку. Забагато розробників досі не дізналися, що вони не повинні ловити виняток, якщо код може викинути лише щось на зразок IOException. У мене в коді помилки повністю проковтнулися, тому що хтось спіймав Виняток. Твердження не потрапляють у цю пастку. Винятки становлять ситуації, які ви очікуєте побачити у виробничому коді. Що стосується ведення журналу, то ви також повинні реєструвати всі свої помилки, навіть якщо помилки трапляються рідко. Наприклад, чи дійсно ви хочете дозволити OutOfMemoryError проходити без реєстрації?
МігельМуноз

14

Ось найпоширеніший випадок використання. Припустимо, ви перемикаєте значення перерахунку:

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

Поки ти розбираєшся у кожному випадку, ти добре. Але коли-небудь хтось додасть фігу до вашого перерахунку і забуде додати його до вашої заяви переключення. Це призводить до появи помилки, яка може виявити складність, оскільки ефекти не будуть відчуватися, поки ви не залишите оператор перемикання. Але якщо ви запишете свій перемикач так, ви можете його зловити негайно:

switch (fruit) {
  case apple:
    // do something
    break;
  case pear:
    // do something
    break;
  case banana:
    // do something
    break;
  default:
    assert false : "Missing enum value: " + fruit;
}

4
Ось чому вам слід увімкнути попередження, а попередження трактувати як помилки. Будь-який на півдорозі пристойний компілятор здатний сказати вам, якщо тільки ви дозволите йому сказати вам, що вам не вистачає перевірки перерахунку, і це зробить це під час компіляції, що невимовно краще, ніж (можливо, один день) з'ясування в час виконання.
Майк Накіс

9
навіщо тут використовувати твердження, а не якесь виняток, наприклад, незаконне обґрунтування?
liltitus27

4
Це призведе до того, що AssertionErrorякщо твердження включені ( -ea). Яка бажана поведінка у виробництві? Мовчазне бездіяльність та потенційна катастрофа пізніше під час страти? Напевно, ні. Я б запропонував явне throw new AssertionError("Missing enum value: " + fruit);.
aioobe

1
Існує хороший аргумент для просто кидання AssertionError. Що стосується належної поведінки у виробництві, вся суть тверджень полягає у тому, щоб уникнути цього у виробництві. Твердження - це інструмент фази розробки для лову помилок, який легко можна видалити з виробничого коду. У цьому випадку немає ніяких причин видаляти його з виробничого коду. Але у багатьох випадках тести на цілісність можуть уповільнити ситуацію. Поклавши ці тести всередині тверджень, які не використовуються у виробничому коді, ви можете написати ретельні тести, не турбуючись про те, що вони уповільнить ваш виробничий код.
МігельМуноз

Здається, це неправильно. IMHO вам не слід використовувати, defaultщоб компілятор міг попередити вас про відсутні справи. Ви можете returnзамість break(для цього може знадобитися вилучення методу), а потім обробити відсутній випадок після перемикання. Таким чином ви отримуєте як попередження, так і можливість assert.
maaartinus

12

Твердження використовуються для перевірки пост-умов і "ніколи не повинні провалюватися" попередні умови. Правильний код ніколи не повинен провалювати твердження; коли вони спрацьовують, вони повинні вказувати на помилку (сподіваємось, у місці, яке знаходиться поруч із місцем, де власне місце проблеми є).

Прикладом твердження може бути перевірка того, що певна група методів викликається в правильному порядку (наприклад, що hasNext()називається раніше next()в an Iterator).


1
Вам не потрібно дзвонити hasNext () перед наступним ().
DJClayworth

6
@DJClayworth: Вам також не потрібно уникати запуску тверджень. :-)
стипендіати Доналу

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в командному рядку та throw new AssertionError().

2
Тож catch (Throwable t)зауваження здатне сприймати порушення тверджень? Для мене це обмежує їх корисність лише у випадку, коли тіло твердження забирає багато часу, що рідко.
Євгеній Сергєєв

1
Я не впевнений, чому це обмежує корисність твердження. Ви ніколи не повинні ловити Throwable, за винятком дуже рідкісних випадків. Якщо вам потрібно спіймати Throwable, але хочете, щоб він не стверджував тверджень, ви можете просто зловити AssertionErrorпершого і повторно скинути його.
МігельМуноз

7

Справжній приклад із класу Stack (з твердження в статтях Java )

public int pop() {
   // precondition
   assert !isEmpty() : "Stack is empty";
   return stack[--num];
}

80
На це нахмуриться C: Затвердження - це те, що дійсно НІКОЛИ не повинно відбуватися - спливання порожнього стека має викинути NoElementsException або щось уздовж рядків. Дивіться відповідь Дональ.
Конерак

4
Я згоден. Хоча це взято з офіційного підручника, це поганий приклад.
DJClayworth


7
Напевно, там витік пам'яті. Вам слід встановити стек [num] = null; для того, щоб GC виконала свою роботу належним чином.
H.Rabiee

3
Я думаю, що в приватному методі було б правильним використовувати твердження, оскільки було б дивно мати винятки за несправності класу чи методу. У публічному методі, зателефонувавши звідкись ззовні, ви не можете реально сказати, як інший код використовує його. Це дійсно перевіряє, чи є Empty () чи ні? Ви не знаєте.
Власек

7

Затвердження дозволяє виявити дефекти коду. Ви можете увімкнути твердження про тестування та налагодження, залишаючи їх вимкнутими, коли ваша програма працює.

Навіщо стверджувати щось, коли знаєш, що це правда? Це правда лише тоді, коли все працює належним чином. Якщо програма має дефект, це може бути насправді неправдою. Виявлення цього раніше в процесі дає вам знати, що щось не так.

assertЗаява містить цю заяву разом з додатковим Stringповідомленням.

Синтаксис твердження про твердження має дві форми:

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

5

Окрім усіх чудових відповідей, наданих тут, офіційний посібник з програмування Java SE 7 має досить стисле керівництво щодо використання assert; з кількома точковими прикладами, коли гарна (і, що важливо, погана) ідея використовувати твердження, і чим це відрізняється від викидання винятків.

Посилання


1
Я згоден. У статті є безліч чудових прикладів. Особливо мені сподобався той, щоб переконатися, що метод викликається лише тоді, коли об’єкт тримає замок.
MiguelMunoz

4

Assert дуже корисний при розробці. Ви використовуєте його, коли щось просто не може статися, якщо ваш код працює правильно. Він простий у використанні і може залишатися в коді назавжди, оскільки він буде вимкнено в реальному житті.

Якщо є якийсь шанс, що стан може виникнути в реальному житті, тоді ви повинні впоратися.

Мені це подобається, але не знаю, як його ввімкнути в Eclipse / Android / ADT. Здається, він вимкнений навіть під час налагодження. (У цьому розділі є потік, але він посилається на "Java vm", який не відображається у налаштуваннях запуску ADT).


1
Щоб увімкнути твердження в затемненні IDE, будь ласка, слідкуйте за tutoringcenter.cs.usfca.edu/resources/…
Ayaz Pasha

Я не думаю, що в Android є спосіб увімкнути твердження. Це дуже невтішно.
МігельМуноз

3

Ось твердження, яке я написав на сервері для проекту Hibernate / SQL. Субстанція має два ефективно булевих властивості, званих isActive та isDefault. Кожен може мати значення "Y" або "N" або null, яке трактувалося як "N". Ми хочемо переконатися, що клієнт браузера обмежений цими трьома значеннями. Отже, у своїх сетерах для цих двох властивостей я додав це твердження:

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

Зауважте наступне.

  1. Це твердження стосується лише фази розвитку. Якщо клієнт надішле погану цінність, ми зловимо це рано і виправимо його, задовго до того, як досягти виробництва. Твердження стосуються дефектів, які можна зловити рано.

  2. Це твердження повільне та неефективне. Нічого страшного. Твердження можуть бути повільними. Нас це не хвилює, оскільки це лише інструменти для розробки. Це не уповільнить виробничий код, оскільки твердження будуть відключені. (Є певна незгода з цього приводу, про яку я пізніше піде.) Це призводить до мого наступного моменту.

  3. Це твердження не має побічних ефектів. Я міг би перевірити свою цінність на незмінюваному статичному фінальному наборі, але цей набір залишився б у виробництві, де він ніколи не звикне.

  4. Це твердження існує для перевірки правильної роботи клієнта. Тож до моменту, коли ми досягнемо виробництва, ми будемо впевнені, що клієнт працює належним чином, тому ми можемо сміливо вимкнути твердження.

  5. Деякі люди запитують це: Якщо твердження не потрібне у виробництві, чому б просто не вийняти їх, коли закінчите? Тому що вони все ще знадобляться, коли ви почнете працювати над наступною версією.

Деякі люди стверджують, що ви ніколи не повинні використовувати твердження, оскільки ви ніколи не можете бути впевнені, що всі помилки зникли, тому вам потрібно тримати їх навколо навіть у виробництві. І тому використовувати сенс твердження не має сенсу, оскільки єдиною перевагою тверджень є те, що ви можете їх вимкнути. Отже, відповідно до цього мислення, ви (майже) ніколи не повинні використовувати твердження. Я не погоджуюсь. Безумовно, правда, що якщо тест належить до виробництва, не слід використовувати твердження. Але цей тест не належить до виробництва. Це - для вилову помилки, яка, ймовірно, ніколи не досягне виробництва, тому її можна безпечно вимкнути, коли закінчите.

До речі, я міг би написати це так:

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

Це добре лише для трьох значень, але якщо кількість можливих значень збільшується, версія HashSet стає більш зручною. Я вибрав версію HashSet, щоб зробити свою думку про ефективність.


Я сильно сумніваюся , що з допомогою такої крихітний а , HashSetприносить будь-яка перевага в швидкості над ArrayList. Більше того, створення та набір списків домінують у часі пошуку. Вони б добре штрафували при використанні константи. Все, що сказали, +1.
maaartinus

Все правда. Я зробив це неефективним способом, щоб проілюструвати свою думку про те, що твердження можуть бути повільними. Це можна зробити більш ефективним, але є й інші, які не можуть. У чудовій книзі під назвою "Написання твердого коду" Стів Магуайр розповідає про твердження в Microsoft Excel для тестування нового коду додаткового оновлення, який пропустив комірки, які не повинні змінюватися. Кожен раз, коли користувач вносив зміни, за твердженням буде перераховано всю електронну таблицю, щоб переконатися, що результати збігаються з результатами функції інкрементального оновлення. Це дійсно сповільнило налагоджувальну версію, але вони зловили всі свої помилки рано.
MiguelMunoz

Повністю погодився. Твердження - це свого роду тести - вони менш універсальні, ніж звичайні тести, але вони можуть охоплювати приватні методи і писати набагато дешевше. Я спробую використовувати їх ще більше.
maaartinus

2

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

Твердження працює під час виконання. Простий приклад, який може пояснити всю концепцію дуже просто, тут - Що робить ключове слово assert на Java? (WikiAnswers).


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" в основному використовується як налагоджувальна програма розробниками програмного забезпечення в процесі налагодження. Сертифікатні повідомлення ніколи не повинні з’являтися. Багато мов пропонують час компіляції, що призведе до ігнорування всіх "тверджень" для використання при створенні "виробничого" коду.

"Винятки" - це зручний спосіб вирішення будь-яких умов помилок, незалежно від того, представляють вони логічні помилки чи ні, оскільки, якщо ви зіткнетесь з умовою помилки, такою, що не можете продовжуватись, ви можете просто "кинути їх у повітря, "звідки б ви не були, очікуючи, що хтось там буде готовий" зловити "їх. Управління передається в один крок прямо від коду, який кинув виняток, прямо до рукавиці лову. (І видовище може побачити повний зворотній зв'язок, що відбувся.)

Крім того, абоненти цієї підпрограми не повинні перевіряти, чи вдалася підпрограма: "якщо ми зараз тут, то, мабуть, це вдалося, тому що в іншому випадку вона б кинула виняток, і ми зараз не були б тут!" Ця проста стратегія значно спрощує розробку коду та налагодження.

Винятки зручно дозволяють умовам фатальної помилки бути такими, якими вони є: "винятки з правила". І, щоб вони обробляли кодовий шлях, який також є "винятком із правила ... " мухоловка! "


1

Твердження - це перевірки, які можуть бути відключені. Вони рідко використовуються. Чому?

  • Вони не повинні використовуватися для перевірки аргументів публічного методу, оскільки ви не маєте контролю над ними.
  • Вони не повинні використовуватися для простих перевірок, як- result != nullот такі перевірки дуже швидкі і навряд чи є що врятувати.

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

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

Іноді чек насправді не дорогий, але в той же час, ти майже впевнений, він пройде. У моєму коді є, наприклад,

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

де я майже впевнений, що список, який я щойно створив, має унікальні елементи, але я хотів його документувати та ще раз перевірити.


0

В основному "стверджувати істину" пройде, а "стверджувати помилкове" не вдасться. Давайте розглянемо, як це буде працювати:

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. Є два типи asserts

  1. Дуже прості assertзаяви
  2. Прості assertзаяви.

За замовчуванням всі assertзаяви не виконуватимуться. Якщо assertвислів отримає помилкове значення, воно автоматично призведе до помилки твердження.


1
Це не дає жодного прикладу реального життя, що є метою питання
rubenafo

1
Ви щойно копіювали пасту з: amazon.com/Programmer-Study-1Z0-803-1Z0-804-Certification/dp/… ?
Корай Тугай
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.