首页 > 解决方案 > 要求和断言有什么区别?

问题描述

Kotlin 1.3带来了一个新特性,即契约,以及随之而来的函数require(),但它看起来与assert(). 这是他们的 KDoc 所说的:

require(value: Boolean)IllegalArgumentException:如果value为假,则抛出一个。

assert(value: Boolean)AssertionError:如果value为 false 并且使用 -ea JVM 选项在 JVM 上启用了运行时断言,则抛出一个。

那么我应该什么时候使用require(),什么时候应该使用assert()呢?

标签: kotlin

解决方案


requireassert以不同的方式工作。为此,您需要深入研究代码。

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)还在后台调用了不同的方法。如果替换assertrequire,您将看到智能转换将成功地将其转换为非 null,因为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), where asassert检查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始终有效,无论 VM 标志如何

这意味着require可以用来帮助编译器进行智能转换,并且使用它比assert确保参数有效要好。而且由于它也可以不受 VM 标志的影响,因此它可以在调试案例之外使用,如 forpas 所述。如果你正在制作一个用 Kotlin 编写的库,你可以用手动抛出替换参数检查require,它仍然可以工作。显然,这是假设 Kotlin 1.3.0,但这不是重点。

它可以在内部使用以确保智能转换按预期工作,但如果不满足条件则抛出异常。

不过要回答你的问题:

  • 当您想要进行参数检查时使用require,即使它在生产中。
  • assert如果您正在进行本地调试并-ea启用该标志,请使用此选项。

推荐阅读