在 Kotlin 中,可空类型扩展函数 是一种强大的特性,允许你为可空类型(T?)定义扩展函数。这些函数可以在可空变量上安全调用,避免了显式的空检查。
1. 基本语法
kotlin
// 为可空类型 String? 定义扩展函数
fun String?.safeLength(): Int {
return this?.length ?: 0 // 使用安全调用和 Elvis 操作符
}
fun main() {
val name: String? = "Kotlin"
val nullName: String? = null
println(name.safeLength()) // 6
println(nullName.safeLength()) // 0 - 不会抛出 NPE
}
2. 与不可空扩展函数的区别
kotlin
// 不可空扩展函数
fun String.uppercaseFirst(): String {
return if (this.isNotEmpty()) {
this[0].uppercase() + this.substring(1)
} else {
this
}
}
// 可空扩展函数
fun String?.safeUppercaseFirst(): String {
return if (!this.isNullOrEmpty()) {
this[0].uppercase() + this.substring(1)
} else {
this ?: "" // 处理 null 情况
}
}
fun main() {
val str: String? = "hello"
// str.uppercaseFirst() // 编译错误:需要不可空类型
println(str.safeUppercaseFirst()) // "Hello"
println(null.safeUppercaseFirst()) // "" - 不会崩溃
}
3. 实际应用示例
示例1:可空集合处理
kotlin
fun <T> Collection<T>?.isNullOrEmpty(): Boolean {
return this == null || this.isEmpty()
}
fun <T> Collection<T>?.safeSize(): Int {
return this?.size ?: 0
}
fun main() {
val list: List<String>? = null
println(list.isNullOrEmpty()) // true
println(list.safeSize()) // 0
}
示例2:链式调用
kotlin
fun String?.toIntOrNull(): Int? {
return try {
this?.toInt()
} catch (e: NumberFormatException) {
null
}
}
fun String?.toIntWithDefault(default: Int = 0): Int {
return this.toIntOrNull() ?: default
}
fun main() {
val input1: String? = "123"
val input2: String? = "abc"
val input3: String? = null
println(input1.toIntWithDefault()) // 123
println(input2.toIntWithDefault()) // 0
println(input3.toIntWithDefault()) // 0
}
示例3:DSL 风格扩展
kotlin
fun <T> T?.whenNotNull(block: (T) -> Unit) {
if (this != null) {
block(this)
}
}
fun <T, R> T?.whenNotNull(block: (T) -> R, default: R): R {
return if (this != null) {
block(this)
} else {
default
}
}
fun main() {
val name: String? = "Alice"
val nullName: String? = null
name.whenNotNull {
println("Name is $it") // 输出: Name is Alice
}
nullName.whenNotNull {
println("This won't print")
}
val length = name.whenNotNull({ it.length }, 0)
println(length) // 5
}
4. 作用域函数扩展
kotlin
fun <T : Any> T?.withNotNull(block: T.() -> Unit) {
this?.block()
}
fun <T : Any, R> T?.letNotNull(block: (T) -> R): R? {
return this?.let(block)
}
fun <T : Any> T?.alsoNotNull(block: (T) -> Unit): T? {
this?.also(block)
return this
}
fun main() {
val nullableValue: String? = "test"
nullableValue.withNotNull {
println(uppercase()) // 输出: TEST
}
val result = nullableValue.letNotNull {
it.length * 2
}
println(result) // 8
}
5. 实用工具函数
kotlin
// 文件路径处理
fun String?.toSafePath(): String {
return this?.trim()?.removeSuffix("/") ?: ""
}
// 日期字符串解析
fun String?.toDateOrNull(): LocalDate? {
return try {
this?.let { LocalDate.parse(it) }
} catch (e: Exception) {
null
}
}
// 数字格式化
fun Number?.formatWithDefault(
pattern: String = "#,##0.00",
default: String = "N/A"
): String {
return try {
DecimalFormat(pattern).format(this ?: return default)
} catch (e: Exception) {
default
}
}
fun main() {
val number: Double? = 1234.567
println(number.formatWithDefault()) // 1,234.57
println(null.formatWithDefault()) // N/A
}
6. 最佳实践
✅ 推荐的做法:
kotlin
// 1. 提供合理的默认值
fun String?.orEmpty(): String = this ?: ""
// 2. 明确表达意图
fun String?.isNotNullOrBlank(): Boolean {
return !this.isNullOrBlank()
}
// 3. 支持链式调用
fun String?.trimOrNull(): String? {
return this?.trim()?.takeIf { it.isNotEmpty() }
}
❌ 避免的做法:
kotlin
// 避免在扩展函数中抛出异常
fun String?.unsafeLength(): Int {
return this!!.length // 不要这样做!
}
// 避免混淆可空和不可空扩展
fun String.unsafeForNullable(): String {
return this // 这个函数不能在 null 上调用
}
7. 性能考虑
kotlin
// 内联扩展函数减少开销
inline fun <T> T?.ifNotNull(block: (T) -> Unit) {
if (this != null) block(this)
}
// 对于频繁调用的简单扩展,使用内联
inline fun String?.isNullOrEmpty(): Boolean {
return this == null || isEmpty()
}
总结
可空类型扩展函数的优点:
- 提升代码可读性 - 将空检查逻辑封装在扩展中
- 减少样板代码 - 避免重复的
if (x != null)检查 - 增强表达力 - 创建领域特定的安全操作
- 支持链式调用 - 在可能为空的链式操作中保持安全
使用建议:
- 为常用操作创建可空扩展
- 提供合理的默认返回值
- 保持函数纯函数特性(无副作用)
- 合理使用内联优化性能
通过合理使用可空类型扩展函数,可以显著提高 Kotlin 代码的安全性和可维护性。