Использование AssertionError и утверждений в java

Я использую утверждения в Java стандартным образом, включив их в своей среде IDE. Поэтому они не являются частью выпуска продукции. В последнее время я видел примеры кода с throw new AssertionError(), и я начал думать о ситуации, когда AssertionError следует использовать вместо утверждения.

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

Для AssertionError применяется обратное.

Я также нахожу AssertionError более практичным в местах в коде, где выполнение не должно быть получено, вместо использования assert false //We should not be here. Особенно, если требуется возвратное значение. Например:

int getFoo(AnEnum a){
    if (a == AnEnum.ONE)
       return bar();
    else if (a == AnEnum.TWO)
       return SOME_VALUE;
    //else
    assert false; //throw new AssertionError();
    return -1; //not necessary when usin AssertionError
}
  • Правильно ли мои рассуждения?
  • Каковы другие различия/примеры использования/лучшие практики/ограничения любого подхода?
  • В отношении предоставления описания в AssertionError - если оно предусмотрено или является просто фактом, что это Error (и типа утверждения) достаточно, чтобы быть более или менее уверенным, что трассировка стека будут предоставлены в случае найденных ошибок?

Ответ 1

В technote "Программирование с утверждениями: Инварианты потока управления" приведен следующий код:

void foo() {
    for (...) {
      if (...)
        return;
    }
    assert false; // Execution should never reach this point!
}

Но также дается следующее примечание:

Примечание. Используйте этот метод с осторожностью. Если инструкция недоступна, как определено в Спецификации языка Java, вы получите ошибку времени компиляции, если попытаетесь утверждать, что она не достигнута. Опять же, приемлемой альтернативой является просто бросить AssertionError.


Вы не можете ожидать, что AssertionError будет сброшен, когда утверждения будут отключены. Поскольку конструкторы AssertionError являются общедоступными, и поскольку, вероятно, нет замены для AssertionError(String message, Throwable cause), я предполагаю, что вы должны ожидать их, даже если они отключены.


Бросок AssertionError на недостижимый код (т.е. без какого-либо реального выражения для оценки) никогда не замедлит код, как предложил Джон Скит, так что это не повредит производительности.


Итак, в конце бросать AssertionError кажется ОК.

Ответ 2

Я бы посоветовал не бросать AssertionError напрямую. Если вы решите положиться на AssertionError для проверки инвариантов, условий до/после, состояний и т.д., Вам все равно лучше использовать регулярные утверждения с флагом "-ea", включенным в процессе производства.
Причина в том, что механизм утверждений (кроме оптимизации на уровне компилятора) дает вам возможность сразу включить или выключить все утверждения. Даже если вы не можете придумать причину, чтобы сделать это сейчас, если вы столкнетесь с какой-либо причиной в будущем, просто подумайте, что вам придется пройти весь свой код типа throw new AssertionError(...) и окружить его скверным if. Вы получите картину.
Так же, как вы не хотите, чтобы магическое число было жестко закодировано во многих местах вашего кода и, вероятно, вместо этого использовало бы константу, вы не должны заражать ваш код множеством дубликатов (т.е. Частью throw new AssertionError(...)).

Еще одно слово об утверждениях. Я считаю, что вам следует подумать дважды, прежде чем полагаться на ошибки утверждения в производственном коде. Причина в том, что AssertionError очень общий. У этого есть сообщение и причина, но это в значительной степени это.
Вместо этого рассмотрите конкретные подклассы RuntimeException, которые будут передавать больше информации как из конкретного класса, более связанного с проблемой, так и путем переноса фактических данных, связанных с проблемой.
В качестве простого примера рассмотрим случай, который вы упомянули в своем вопросе, где есть часть кода, которого вы не ожидаете достичь. Утверждение или AssertionError означало бы, что вы достигли неожиданного кода, но не намного больше. Использование определенного RuntimeException может также доставить состояние локальных переменных и параметров метода в этот момент времени. Вы можете утверждать, что это выполнимо с установкой сообщения об утверждении или AssertionError, чтобы содержать эту информацию, но это не работает при использовании автоматических механизмов регистрации ошибок/обработки ошибок. Такие механизмы могут обрабатывать неожиданное поведение с использованием шаблона посетителя в разных подклассах RuntimeException, которые вы используете для проверки неожиданного поведения (по дескриптору я также имею в виду отказ, не обязательно восстановление).