可空类型的扩展
有时事情并不是看起来的那样。
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 找到。