В чем разница между require и assert? - PullRequest
0 голосов
/ 04 ноября 2018

С Kotlin 1.3 появилась новая функция, контракты, а с ними и функция require(), но, похоже, она очень похожа на assert(). Вот что говорит их KDoc:

require(value: Boolean): выдает IllegalArgumentException, если value ложно.

assert(value: Boolean): выдает AssertionError, если value имеет значение false и утверждения JVM включены во время выполнения с использованием параметра -ea JVM.

Так, когда я должен использовать require() и когда я должен использовать assert()?

Ответы [ 2 ]

0 голосов
/ 04 ноября 2018

require и assert работают по-разному. Для этого вам необходимо погрузиться в код.

assert(condition) вызывает другой метод внутри себя, где вы видите действительный код:

@kotlin.internal.InlineOnly
public inline fun assert(value: Boolean, lazyMessage: () -> Any) {
    if (_Assertions.ENABLED) {
        if (!value) {
            val message = lazyMessage()
            throw AssertionError(message)
        }
    }
}

AFAIK, это связано с флагом -ea; если -ea отсутствует (или отключен), assert не сгенерирует исключение.

В результате это не скомпилируется:

fun something(string: String?){
    assert (string != null)
    nonNull(string) // Type mismatch
}
fun nonNull(str: String){} 

Это то место, где требуется require. require(condition) также вызывает другой метод изнутри. Если вы замените assert на require, вы увидите, что умное приведение успешно преобразует его как ненулевое, потому что require гарантированно выдает исключение в случае сбоя условия.

@kotlin.internal.InlineOnly
public inline fun require(value: Boolean, lazyMessage: () -> Any): Unit {
    contract {
        returns() implies value
    }
    if (!value) {
        val message = lazyMessage()
        throw IllegalArgumentException(message.toString())
    }
}

Только логическая функция выполняет контракт, а затем вызывает этот метод в случае сбоя контракта.

Контракты являются новыми, и я не совсем уверен, как они работают, но я так понимаю:

Ключевое слово implies является infix fun; что он делает, так это говорит компилятору, что условие истинно, если он возвращается из метода. Это помогает с автоматическим приведением, как в примере, который я упоминал ранее. Это на самом деле не вызывает возврата метода (или, по крайней мере, на это указывает мое текущее тестирование), но это для компилятора.

Это также читабельно: returning implies condition is true

Это контактная часть: здесь всегда выдается исключение, как вы можете видеть по условию. require использует if(!value), где assert проверяет if(_Assertions.ENABLED && !value).

Это не единственное использование для require. Это также может быть использовано для проверки аргументов. То есть если у вас есть это:

operator fun get(index: Int) : T {
    if (index < 0 || index >= size) 
        throw IllegalArgumentException("Index out of range")
    // return here
}

Вы можете заменить его на:

operator fun get(index: Int) : T {
    require (index >= 0 && index < size) { "Index out of range" }
    // return here
}

Для этого есть много разных применений, но это только некоторые примеры.

Что это значит:

  • assert похоже на Java; он срабатывает только в том случае, если утверждения включены. Использование его не гарантирует выполнение условия.
  • require всегда работает, независимо от флагов ВМ

Это означает, что require может быть использован для помощи компилятору, т.е. умному приведению, и лучше использовать, чем assert, чтобы убедиться, что аргументы верны. И поскольку он также работает независимо от флагов виртуальной машины, его можно использовать вне случаев отладки, как уже упоминалось в forpas. Если вы создаете библиотеку, написанную на Kotlin, вы можете заменить проверку аргументов на ручную передачу на require, и она все равно будет работать. Очевидно, это предполагает Kotlin 1.3.0, но это не относится к делу.

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

Чтобы ответить на ваш вопрос, хотя:

  • Используйте require, когда вы хотите проверять аргументы, даже если он находится в производстве.
  • Используйте assert, если вы выполняете локальную отладку, и у вас включен флаг -ea.
0 голосов
/ 04 ноября 2018

Допустим, вы хотите, чтобы функция вычисляла n! (факториал) следующим образом:

fun factorial(n: Long): Long {
    require(n >= 0) { "Number must no be negative" }
    // code
}

В этом случае require() проверяет достоверность аргумента, переданного в функцию , и выдает IllegalArgumentException, если аргумент не тот, каким он должен быть, и для отладки у вас также есть пояснительное сообщение ,

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

Существует также check(Boolean) выдает IllegalStateException, если его аргумент ложный,
который используется для проверки состояния объекта.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...