Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

可空类型的扩展

有时事情并不是看起来的那样。

s?.f() 暗示了 s 是可空的,否则你可以简单地调用 s.f()。类似地,t.f() 看起来似乎是 t 是非空的,因为 Kotlin 不要求使用安全调用或编程检查。然而,t 并不一定是非空的。

Kotlin 标准库提供了 String 的扩展函数,包括:

  • isNullOrEmpty():检查接收者 String 是否为 null 或为空。
  • isNullOrBlank():执行与 isNullOrEmpty() 相同的检查,并且允许接收者 String 仅由空白字符组成,包括制表符(\t)和换行符(\n)。

下面是这些函数的基本测试:

// NullableExtensions/StringIsNullOr.kt
import atomictest.eq

fun main() {
  val s1: String? = null
  s1.isNullOrEmpty() eq true
  s1.isNullOrBlank() eq true

  val s2 = ""
  s2.isNullOrEmpty() eq true
  s2.isNullOrBlank() eq true

  val s3: String = " \t\n"
  s3.isNullOrEmpty() eq false
  s3.isNullOrBlank() eq true
}

函数的名称表明它们适用于可空类型。然而,即使 s1 是可空的,您也可以调用 isNullOrEmpty()isNullOrBlank() 而不使用安全调用或显式检查。这是因为这些函数是可空类型 String? 的扩展函数。

我们可以将 isNullOrEmpty() 重写为一个非扩展函数,该函数以可空 String s 作为参数:

// NullableExtensions/NullableParameter.kt
package nullableextensions
import atomictest.eq

fun isNullOrEmpty(s: String?): Boolean =
  s == null || s.isEmpty()

fun main() {
  isNullOrEmpty(null) eq true
  isNullOrEmpty("") eq true
}

由于 s 是可空的,我们明确地检查了 null 或空。表达式 s == null || s.isEmpty() 使用了短路求值:如果表达式的第一部分是 true,则不会评估表达式的其余部分,从而防止了 null 指针异常。

扩展函数使用 this 来表示接收者(被扩展类型的对象)。为了使接收者可空,将 ? 添加到被扩展类型的类型中:

// NullableExtensions/NullableExtension.kt
package nullableextensions
import atomictest.eq

fun String?.isNullOrEmpty(): Boolean =
  this == null || isEmpty()

fun main() {
  "".isNullOrEmpty() eq true
}

isNullOrEmpty() 作为扩展函数更易读。

  • -

在使用可空类型的扩展时要小心。它们非常适用于简单的情况,例如 isNullOrEmpty()isNullOrBlank(),尤其是使用表达接收者可能是 null 的自我解释性名称。一般来说,最好声明常规(非可空)的扩展。安全调用和显式检查可以澄清接收者的可空性,而针对可空类型的扩展可能会隐藏可空性,令代码的读者(可能是“未来的你”)困惑。

练习和答案可以在 www.AtomicKotlin.com 找到。