Чи краще використовувати assrt або IllegalArgumentException для необхідних параметрів методу?


87

На Java, що більше рекомендується, і чому? Обидва типи будуть кидати винятки, тому в цьому відношенні поводження з ними однакове. assertтрохи коротше, але я не впевнений, наскільки це важливо.

public void doStuff(Object obj) {
    assert obj != null;
    ...
}

проти

public void doStuff(Object obj) {
    if (obj == null) {
        throw new IllegalArgumentException("object was null");
    }
    ...
}

Я віддаю перевагу простому obj.hashCode()замість цього ;-)
Марко

Відповіді:


117

ПОДЕРЖАЙТЕ!

Ствердження видаляються під час виконання, якщо ви чітко не вказали "включити твердження" при складанні коду. Затвердження Java не повинні використовуватися у виробничому коді і повинні бути обмежені приватними методами (див. Виняток проти твердження ), оскільки очікується, що приватні методи будуть відомі та використовуються лише розробниками. Також assertвикине AssertionError, який Errorне поширюється Exception, і який зазвичай вказує на те, що у вас є дуже ненормальна помилка (наприклад, "OutOfMemoryError", яку важко відновити, чи не так?), Від вас не очікується можливість лікувати.

Видаліть прапор "утвердити твердження" та перевірте за допомогою налагоджувача, і ви побачите, що не будете наступати на виклик кидка IllegalArgumentException ... оскільки цей код не був скомпільований (знову ж таки, коли "ea" видалено)

Краще використовувати другу конструкцію для публічних / захищених методів, і якщо ви хочете щось зробити в одному рядку коду, є принаймні один спосіб, про який я знаю. Я особисто використовую клас Spring Framework , Assertякий має декілька методів перевірки аргументів, які кидають "IllegalArgumentException" при відмові. В основному, ви робите це:

Assert.notNull(obj, "object was null");

... Який насправді буде виконувати точно той самий код, який ви написали у своєму другому прикладі. Є кілька інших корисних методів, таких як hasText, hasLengthтам.

Мені не подобається писати більше коду, ніж потрібно, тому я щасливий, коли скорочую кількість записаних рядків на 2 (2 рядки> 1 рядок) :-)


Ах, я забув про вилучення тверджень! Чудова відповідь. Я зачекаю трохи, щоб побачити, чи все ще заходить, тоді прийміть це :)
Daenyth

2
Зауважте, що не існує прапора, який би видаляв твердження під час компіляції (хоча вони можуть бути видалені за умови умовного дотримання ). Твердження вимкнено під час виконання за замовчуванням (я думаю, що JVM трактує їх як NOOP), але їх можна вмикати за допомогою java -eaта програмним шляхом. @Jalayn Я думаю, що твердження у виробничому коді цілком справедливі, оскільки вони корисні для налагодження у цій галузі
Джастін Мюллер

@Jalayn, -1. Компілятор не видаляє код ствердження. Навіть якщо вони не запускаються, якщо ви не зробите cmd java -ea.
Pacerier

5
немає необхідності у весняних рамках, коли можна використовуватиObjects.requreNonNull
cambunvolu

46

Потрібно використовувати виняток. Використання твердження було б неправильним використанням функції.

Неперевірені винятки призначені для виявлення помилок програмування користувачів вашої бібліотеки, тоді як твердження призначені для виявлення помилок у вашій власній логіці. Це окремі питання, які не слід змішувати.

Наприклад, твердження

assert myConnection.isConnected();

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

З іншого боку, чек

if (!myConnection.isConnected()) {
    throw new IllegalArgumentException("connection is not established");
}

означає, що "Виклик моєї бібліотеки без встановлення з'єднання є помилкою програмування".


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

4
відмінна точка "Неперевірені винятки призначені для виявлення помилок програмування користувачів вашої бібліотеки, тоді як твердження призначені для виявлення помилок у вашій власній логіці. Це окремі проблеми, які не слід змішувати."
Асим Гаффар

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

11

Якщо ви пишете функцію, яка не дає значення nullяк дійсне значення параметра, слід додати @Nonnullанотацію до підпису та використовувати, Objects.requireNonNullщоб перевірити, чи є аргумент, nullі кинути а, NullPointerExceptionякщо він є. @NonnullАнотація розроблена для документації і надасть корисні попередження під час компіляції в деяких випадках. Це не заважає nullпроходити під час виконання.

void doStuff(@Nonnull Object obj) {
    Objects.requireNonNull(obj, "obj must not be null");

    // do stuff...
}

Оновлення:@Nonnull анотації не є частиною стандартної бібліотеки Java. Натомість існують численні конкуруючі стандарти з бібліотек сторонніх розробників (див. Яку анотацію на @NotNull Java я повинен використовувати? ). Це не означає, що це погана ідея використовувати його, лише те, що це не стандартно.


Дякую, що вказали Objects.requireNonNull(), що було для мене новим. Чи знаєте ви, чи є десь подібний метод "RequTrue ()" або "RequEqual ()"? Нічого проти Spring's Assert, але не всі використовують це.
user949300

@ user949300 Objects.requireNonNull()призначений для перевірки аргументів. Якщо аргумент потрібно trueчимось дорівнює або дорівнює, то аргумент безглуздий. Для випадків помилок , відмінних від незаконних аргументів, ви повинні , що більш точно описує помилку. Є також JUnit, але це для тестів. throwExceptionAssert
камбунфекційний

Я думав більше, як, скажімо, для функції квадратного кореня Objects.requireTrue(x >= 0.0);або для якогось хеша,Objects.requireEquals(hash.length == 256)
user949300

Який @Nonnull я повинен використовувати? javax.validation.constraints.NotNull?
Aguid

Я б використав анотації Eclipse JDT , оскільки їх роблять розумні хлопці :). Документація: help.eclipse.org/neon/… - Ви також можете налаштувати IntelliJ для їх використання.
коппор

2

Я завжди вважаю за краще кидати IllegalArgumentException над твердженнями.

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

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


5
Затвердження були близько 40 років до JUnit - запитайте програміста C про макроси ASSERT.
JBRWilkinson

3
це питання не стосується с; про Java. Тому я відповів у контексті Java.
rai.skumar

Не плутайте assert (зарезервоване ключове слово) проти Assert (клас JUnit), вони обидва використовуються для перевірки змінної, але крім того, що це дві дуже різні речі і поводяться дуже по-різному.
Ньютопський

1

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


1

Я мало використовую асерти, але загальний підхід з Lombock @NonNull: https://projectlombok.org/features/NonNull

Реалізація Lombok: імпортувати lombok.NonNull;

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    this.name = person.getName();
  }
}

Версія Java:

 import lombok.NonNull;

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    if (person == null) {
      throw new NullPointerException("person");
    }
    this.name = person.getName();
  }
}

Lombok - це дуже приємна бібліотека, яку я використовую всюди

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.