什么是正确使用!!操作员?

What is a correct use of the !! operator?

我了解 Kotlin 的 !! 运算符的作用。我想知道是否有任何用例最好使用 !! 而不是 ?.let?: return,或其他一些更安全的空检查。

Kotlin documentation on null safety 只是说它是为喜欢 NullPointerExceptions 的人准备的;为什么人们会想要那些?

我只是很好奇是否有它的用例,因为没有更好的选择。

不是每个人都是初学者。为该运算符使用感叹号有助于表示如果您确实使用它,您会更好地了解自己在做什么。

在某些情况下,它会产生更清晰的代码。我在使用第三方库时经常看到这种情况。

例如,Android 的 LiveData.value 可以为空,但如果您在初始化时正确设置该值,那么您就知道该值永远不能为空。

val myLiveData = MutableLiveData<Float>().apply{ value = 1f }

这是一个见仁见智的问题,但我认为将对这个 getter 的每个引用都用未使用的默认值乱扔会很吵:

when ((myLiveData.value ?: 1f) > 0f) { /* */ }

when (myLiveData.value!! > 0f) { /* */ }

就我个人而言,如果我使用来自 class 之外的实时数据,我不会使用双爆炸,因为这样就无法明确保证它确实有一个值集。但是从包含它的 class 中,您可以相信自己知道自己在做什么。

这是来自 Android 的另一个例子。当您在 PreferenceFragmentCompat 中工作并且需要获取 Preference 引用时,如果首选项不在层次结构中,则使用 findPreference which returns null。如果您知道 Preference 应该在那里,那么使用 !! 是有意义的,因为您 想要 查看实际上不存在的异常。或者,您可以使用 ?: error("missing preference"),但它是额外的代码,并没有真正使它在堆栈跟踪中更明显地显示出了什么问题。或者您可能想使用 findPreference("x")?.let { //... },但当缺少首选项时它会默默地失败,如果您知道首选项应该在那里,这不是您想要的。

当然要注意使用!!。如果您的特定 PreferenceFragment 通过在各种情况下添加和删除它们来对首选项进行一些操作,那么在测试期间使用 ?.let {/**/} ?: Log.e(...) 可能更合适,因为您错过了首选项不存在的情况你以为会是。

只要 null 值指示代码本身不正确,您就会希望抛出 NullPointerException。正如如果无效参数表明调用者的代码不正确,您可能会显式抛出 IllegalArgumentException。

因此,!! 的用例与 if(...) { throw ...; } 的用例非常相似,但 !! 更短。

如果代码不正确,您希望知道,立即使用堆栈跟踪停止程序是 (1) 通知程序员的好方法问题,以及 (2) 为调试目的提供尽可能多的有用信息。