requireNotNull 与 sure 运算符!!在科特林

requireNotNull vs sure operator !! in Kotlin

我们有一个基于 Kotlin 的应用程序,最近我们添加了第三方代码质量工具(Codacy 中的 Detekt)。但是,我们开始面临 UnsafeCallOnNullableType 错误。 我们发现可行的方法是对所有可能为 null 的参数添加 requireNotNull 检查。目前,我们正在使用确定运算符 (!!)

我们是否有任何特定的理由或惯例来选择一个而不是另一个。据我所知,两者都会抛出异常并阻止执行流程,除了一个会抛出 IllegalArgumentException 而另一个会抛出 NullPointerException。

根据我的经验,当值可为 null 时,最佳做法是使用 ??: 运算符,并在值为 null 时提供替代方案(如果可用),例如:

settings?.getValue("some-setting") ?: defaultValue

请注意,当 settingsgetValue return 为空时,此表达式将 return defaultValue

最好尽量避免使用 !! 运算符,因为这基本上会破坏任何针对空值的保护。当这不可能时,我会抛出一个更全面的异常,而不是依赖像 IAE 或 NPE 这样的通用异常,例如:

settings?.getValue("some-setting") ?: throw SettingNotFound("Descriptive message")

requireNotNull,假设您指的是 Objects#requireNonNull,是一个 Java 方法,它等同于 !!,但有一个不同的例外。

您没有添加任何代码,所以帮助您调试有点困难。您提到了第三方代码质量工具,但没有提到是哪个。我偶然发现 this GH issue 与您遇到的错误相匹配。这也是我能找到的唯一会在任何时候使用该确切错误的东西。我可能漏掉了一些,但它涵盖了 Google 的热门歌曲,所以我要放弃它。

如果您使用的是 Detekt,这是一个已报告的错误。 IntelliJ 甚至建议使用 !!

但是,您可以采用其他方式。

是的,使用 Objects#requireNonNull 是一种选择。不过还有第二个,它使用空安全运算符,正如 m0skit0 提到的那样。

之所以可行,是因为如果调用的任何内容为空,则最终结果为空。 IE。这个:

instance.nonNullType.nullable?.nullableChild?.someOtherNullableChild

如果有一个可空的为null,则最终结果为null,调用其他none个

现在,考虑到这可能是检测中的错误,这似乎是目前最简单的解决方法:

whatever.calls.you?.make?.to?.the?.database ?: throw NullPointerException("Something is null");

它还保持变量非空,这意味着您以后不需要空安全调用。 elvis 运算符检查是否有任何内容为空,然后抛出异常。或者,您可以只使用 Objects#requireNotNull:

Objects.requireNonNull(whatever.calls.you.make.to.the.database)

如果你真的需要验证每一步,你只需要在所有地方都进行空检查

TL;DR:

!!requireNotNull 在它们的工作方式上实际上是相同的,除了 requireNotNull 是一个方法调用并且 !! 编译为一个 if 语句:

if(whatever == null) {
    Intrinsics.throwNpe();
}

!! 触发 UnsafeCallOnNullableType 的原因是因为 Detekt 中的一个(可能的)错误。不过,这两个选项都是同一事物的语法糖:如果变量为 null,则抛出 NPE。

如您所述,requireNotNull() 抛出 IllegalArgumentException,!! 抛出 NullPointerException。如果您想区分开发人员添加的防御代码与未防御代码(通过使用 !! 不是很明显),这可能会有所帮助。

然而,使用 requireNotNull() 的更大好处是使用带有 lazyMessage 参数的函数。这样您的开发人员就可以向异常添加更多有意义的消息,这有助于调试