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
.