2-2-19 快速掌握Kotlin-可空类型扩展函数

在 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()
}

总结

可空类型扩展函数的优点:

  1. 提升代码可读性 - 将空检查逻辑封装在扩展中
  2. 减少样板代码 - 避免重复的 if (x != null) 检查
  3. 增强表达力 - 创建领域特定的安全操作
  4. 支持链式调用 - 在可能为空的链式操作中保持安全

使用建议:

  • 为常用操作创建可空扩展
  • 提供合理的默认返回值
  • 保持函数纯函数特性(无副作用)
  • 合理使用内联优化性能

通过合理使用可空类型扩展函数,可以显著提高 Kotlin 代码的安全性和可维护性。

相关推荐
成都大菠萝4 小时前
2-2-18 快速掌握Kotlin-扩展属性
android
成都大菠萝4 小时前
2-2-21 快速掌握Kotlin-定义扩展文件
android
成都大菠萝4 小时前
2-2-23 快速掌握Kotlin-apply函数详解
android
2501_916007474 小时前
iOS 证书如何创建,从能生成到能长期使用
android·macos·ios·小程序·uni-app·cocoa·iphone
Just_Paranoid5 小时前
【AOSP】Android Dump 信息快速定位方法
android·adb·framework·service·aosp·dumpsys
帅得不敢出门5 小时前
MTK Android11获取真实wifi mac地址
android·mtk
成都大菠萝5 小时前
2-2-16 快速掌握Kotlin-泛型扩展函数
android
I'm Jie5 小时前
Gradle 多模块依赖集中管理方案,Version Catalogs 详解(Kotlin DSL)
android·java·spring boot·kotlin·gradle·maven
BoomHe5 小时前
Android 13 (API 33)开发自己的 Settings ,如何配置 WiFi BT 权限
android