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

какие-то реальные примеры из жизни чтобы понять ключевую роль утверждения?

17 ответов


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

пример:

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

должен быть без ошибок (при условии, что 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-аналогичная концепция, но они не могут проверить все. Если вы хотите еще больше проверок (за счет скорости выполнения), вы должны использовать утверждения. Это коптит ваш код, но вы можете в конце концов доставить продукт в удивительно короткое время разработки (чем раньше вы исправите ошибку, тем ниже стоимость). И кроме того: если в вашем коде есть какая-либо ошибка, Вы ее обнаружите. Нет никакого способа проскальзывания ошибки и вызвать проблемы позже.

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


утверждения являются инструментом фазы разработки, чтобы поймать ошибки в коде. Они предназначены для легкого удаления, поэтому они не будут существовать в производственном коде. Таким образом, утверждения не являются частью "решения", которое вы предоставляете клиенту. Это внутренние проверки, чтобы убедиться, что ваши предположения верны. Самым распространенным примером является проверка на null. Многие методы написаны так:

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

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

/**
 * @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
  }
}

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

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

/**
 * 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() метод в случае, когда это значение не null. Но это предполагает equals() метод правильно выполняет контракт equals() путем правильной обработки параметра null.

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

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() метод не проверяет значение null, как того требует его контракт.

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. в разработке вы всегда должны включать утверждения, даже если написанный вами код не использует утверждения. Моя IDE настроена всегда делать это по умолчанию для любого нового исполняемого файла.

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

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


много хороших ответов, пояснив, что 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. Они отказались включить в производственный код базовые проверки и утверждения, и мы продолжаем платить цену.)


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

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

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

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


что делает ключевое слово 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() is true, когда -ea передается в командной строке, и false в противном случае.

мы используем: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().

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

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

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

ссылке


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

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

An 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. проверка условий post в конце любого метода. Это означает, что после выполнения бизнес-логики можно использовать утверждения для обеспечения внутреннего состояния переменных или результатов в соответствии с тем, что вы ожидаете. Например, метод, открывающий сокет или файл, может использовать утверждение в конце, чтобы убедиться, что сокет или файл действительно открыт.

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

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

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

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

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


вот утверждение, которое я написал на сервере для проекта 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, поскольку единственное преимущество утверждает, что вы можете отключить их. Следовательно, согласно этому мышлению, вы (почти) никогда не должны использовать утверждения. Я не согласен. Конечно, верно, что если тест принадлежит производству, вы не должны использовать assert. Но этот тест делает не принадлежат на производстве. Это для ловли ошибки, которая вряд ли когда-либо достигнет производства, поэтому ее можно безопасно отключить, когда вы закончите.

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

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

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


утверждения по умолчанию отключены. Чтобы включить их, мы должны запустить программу с -ea options (степень детализации может быть различной). Например, 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 может быть любым выражением, возвращающим значение, которое я использовал это чаще всего просто для печати сообщения.

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

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

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

кроме того, вызывающие эту подпрограмму не должны проверьте, удалось ли выполнить подпрограмму:


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

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


в принципе," 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;
    }
}

assert - ключевое слово. Он был представлен в JDK 1.4. Это два типа asserts

  1. очень просто assert заявления
  2. простой assert заявления.

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