Лучше использовать assert или IllegalArgumentException для обязательных параметров метода?


88

В 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()Вместо этого я предпочитаю простой ;-)
— Марко

Ответы:


119

BEWARE!

Утверждения удаляются во время выполнения, если вы явно не укажете «включить утверждения» при компиляции кода. Утверждения 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 Я думаю, что наличие утверждений в производственном коде совершенно правильно, так как они полезны для отладки в полевых условиях
— Джастин Мюллер,

@Джалайн, -1. Компилятор не удаляет код подтверждения. Даже если они не будут запущены, если вы не сделаете cmd java -ea.
— Pacerier

5
нет необходимости в Spring Framework, когда вы можете использоватьObjects.requreNonNull
— cambuncsive

46

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

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

Например, утверждение

assert myConnection.isConnected();

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

С другой стороны, чек

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

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


1
Эта информация действительно полезна, но я принимаю Jalayn, так как она указывает, как я могу потенциально представить ошибку с помощью метода assert.
— Дейнит

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

Верно, но это предполагает, что OP пишет библиотеку. Если их код только для внутреннего использования, assert приемлемо.
— user949300

11

Если вы пишете функцию, которая не допускает nullв качестве допустимого значения параметра, вы должны добавить @Nonnullаннотацию к сигнатуре и использовать ее, Objects.requireNonNullчтобы проверить, является ли аргумент аргументом, nullи бросить a, NullPointerExceptionесли он есть. @NonnullАннотации для документации и будут предоставлять полезные предупреждения во время компиляции в некоторых случаях. Это не мешает nullбыть переданным во время выполнения.

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

    // do stuff...
}

Обновление:@Nonnull аннотации не является частью стандартной библиотеки Java. Вместо этого существует множество конкурирующих стандартов от сторонних библиотек (см. Какую аннотацию @NotNull Java следует использовать? ). Это не значит, что это плохая идея, просто это не стандартно.


Спасибо за указание Objects.requireNonNull(), что было новым для меня. Вы знаете, есть ли где-нибудь похожий метод requireTrue () или requireEqual ()? Ничего против 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 - спросите программиста на Си о макросах ASSERT.
— JBRWilkinson

3
этот вопрос не о с; это о Java. Так что я ответил в контексте Java.
— rai.skumar

Не путайте assert (зарезервированное ключевое слово) с Assert (класс JUnit), они оба используются для проверки переменной, но помимо этого это две совершенно разные вещи и ведут себя совершенно по-разному.
— Newtopian

1

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


1

Я не часто использую aserts, но общий подход с 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.