Лучше использовать 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");
    }
    ...
}
Daenyth
источник
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 строка) :-)

Jalayn
источник
Ах, я забыл об удалении утверждений! Отличный ответ. Я подожду немного, чтобы увидеть, если что-нибудь еще придет, затем приму это :)
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");
}

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

dasblinkenlight
источник
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 следует использовать? ). Это не значит, что это плохая идея, просто это не стандартно.

cambunctious
источник
Спасибо за указание 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.

rai.skumar
источник
5
Утверждения были примерно за 40 лет до JUnit - спросите программиста на Си о макросах ASSERT.
JBRWilkinson
3
этот вопрос не о с; это о Java. Так что я ответил в контексте Java.
rai.skumar
Не путайте assert (зарезервированное ключевое слово) с Assert (класс JUnit), они оба используются для проверки переменной, но помимо этого это две совершенно разные вещи и ведут себя совершенно по-разному.
Newtopian
1

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

0lukasz0
источник
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 - действительно неплохая библиотека, которую я использую везде

Kensai
источник