空安全与类型系统

第2章:空安全与类型系统

本章学习目标

  • 深入理解 Kotlin 的空安全机制
  • 掌握可空类型与非空类型的处理方式
  • 理解类型转换与智能转换的工作原理
  • 掌握泛型与型变的高级用法
  • 能够构建类型安全的网络请求封装

2.1 可空类型与非空类型

2.1.1 空安全的核心理念

Kotlin 的设计哲学:在编译时发现空指针异常,而不是运行时。

kotlin 复制代码
// Java 的痛点 - 运行时才能发现 NPE
String name = null;
int length = name.length(); // 运行时崩溃:NullPointerException

// Kotlin 的解决方案 - 编译时就能发现问题
val name: String = null // 编译错误:Null can not be a value of a non-null type String

类型系统的基础

  • String:非空类型,不能为 null
  • String?:可空类型,可以为 null
kotlin 复制代码
// 非空类型
val nonNullName: String = "张三"
// nonNullName = null // 编译错误

// 可空类型
val nullableName: String? = "李四"
nullableName = null // 正确

2.1.2 ? 操作符的使用

? 操作符:标记类型为可空类型。

kotlin 复制代码
// 定义可空类型变量
var name: String? = null

// 定义可空类型参数
fun greet(name: String?) {
    if (name != null) {
        println("你好,$name")
    } else {
        println("你好,访客")
    }
}

// 定义可空类型返回值
fun getUserName(userId: Int): String? {
    val users = mapOf(
        1 to "张三",
        2 to "李四"
    )
    return users[userId] // 可能返回 null
}

// 使用
val name1 = getUserName(1) // 返回 "张三"
val name2 = getUserName(3) // 返回 null

可空类型的限制

kotlin 复制代码
val name: String? = "张三"

// 不能直接调用可空类型的方法
// val length = name.length // 编译错误:Only safe (?.) or non-null asserted (!!.) calls are allowed

// 不能直接赋值给非空类型
// val nonNull: String = name // 编译错误:Type mismatch

2.1.3 安全调用(?.)与 Elvis 操作符(?:)

安全调用操作符(?.)

  • 如果对象不为 null,调用方法
  • 如果对象为 null,返回 null
kotlin 复制代码
val name: String? = "张三"

// 安全调用
val length = name?.length // 如果 name 为 null,返回 null,否则返回 length

// 链式安全调用
val cityLength = user?.profile?.address?.city?.length

// 实际应用
class User(val name: String, val profile: Profile?)
class Profile(val address: Address?)
class Address(val city: String?)

val user = User("张三", Profile(Address("北京")))
val city = user?.profile?.address?.city // 返回 "北京"

val user2 = User("李四", null)
val city2 = user2?.profile?.address?.city // 返回 null

Elvis 操作符(?:)

  • 如果左侧表达式不为 null,返回左侧值
  • 如果左侧表达式为 null,返回右侧值
kotlin 复制代码
val name: String? = null

// Elvis 操作符
val displayName = name ?: "匿名用户" // name 为 null,返回 "匿名用户"

val name2: String? = "张三"
val displayName2 = name2 ?: "匿名用户" // name2 不为 null,返回 "张三"

// 与安全调用结合
val city = user?.profile?.address?.city ?: "未知城市"

// 实际应用 - 获取用户昵称
fun getUserNickname(user: User?): String {
    return user?.profile?.nickname ?: "匿名用户"
}

// 与 throw 结合
fun getUserNameOrThrow(userId: Int): String {
    return getUserById(userId) ?: throw IllegalArgumentException("用户不存在")
}

安全调用与 Elvis 操作符的组合

kotlin 复制代码
// 场景:获取用户头像 URL,如果没有则使用默认头像
fun getUserAvatar(user: User?): String {
    return user?.profile?.avatarUrl ?: "https://default.avatar.com"
}

// 场景:计算字符串长度,如果为 null 则返回 0
fun getStringLength(str: String?): Int {
    return str?.length ?: 0
}

// 场景:执行操作,如果对象为 null 则不执行
user?.profile?.updateNickname("新昵称")

2.1.4 非空断言(!!)的风险

非空断言操作符(!!)

  • 强制将可空类型转换为非空类型
  • 如果对象为 null,抛出 NullPointerException
kotlin 复制代码
val name: String? = "张三"

// 非空断言
val length = name!!.length // 如果 name 为 null,抛出 NPE

// 风险场景
val name2: String? = null
val length2 = name2!!.length // 运行时崩溃:NullPointerException

什么时候使用 !!

  • 确定对象不会为 null(但编译器无法推断)
  • 性能敏感场景(避免额外检查)
  • 与 Java 代码交互,已知 Java 方法不会返回 null

使用场景示例

kotlin 复制代码
// 场景1:与 Java 代码交互
// Java 方法:public String getName() { return "张三"; } // 保证不会返回 null
val name: String? = javaObject.getName() // Kotlin 认为可能为 null
val length = name!!.length // 我们知道不会为 null,使用 !!

// 场景2:已经检查过 null
fun processName(name: String?) {
    if (name != null) {
        // name 已确认不为 null,可以使用 !!
        val length = name!!.length
        println("名字长度:$length")
    }
}

// 更好的方式:使用智能转换(后面会讲)
fun processNameBetter(name: String?) {
    if (name != null) {
        val length = name.length // 智能转换,不需要 !!
        println("名字长度:$length")
    }
}

!! 的风险

kotlin 复制代码
// 危险的使用方式
val name: String? = getUserInput()
val length = name!!.length // 如果用户输入为空,崩溃

// 安全的方式
val length = name?.length ?: 0 // 使用安全调用和 Elvis 操作符

2.1.5 空安全最佳实践

原则1:优先使用非空类型

kotlin 复制代码
// 好
fun greet(name: String) { // 非 null
    println("你好,$name")
}

// 避免
fun greet(name: String?) { // 可 null
    println("你好,$name")
}

原则2:谨慎使用 !!

kotlin 复制代码
// 避免
val name: String? = getName()
val length = name!!.length

// 推荐
val name: String? = getName()
val length = name?.length ?: 0

原则3:合理使用默认值

kotlin 复制代码
// 推荐
fun getUserName(userId: Int): String {
    return getUserById(userId) ?: "未知用户"
}

// 推荐
fun getUserAvatar(user: User?): String {
    return user?.profile?.avatarUrl ?: getDefaultAvatar()
}

原则4:使用早期返回(Early Return)

kotlin 复制代码
// 不好的方式 - 嵌套 if
fun processUser(user: User?) {
    if (user != null) {
        if (user.profile != null) {
            if (user.profile.address != null) {
                println(user.profile.address.city)
            }
        }
    }
}

// 好的方式 - 早期返回
fun processUserBetter(user: User?) {
    val address = user?.profile?.address ?: return
    println(address.city)
}

2.2 类型转换与智能转换

2.2.1 is 操作符与自动类型转换

is 操作符:检查对象是否为某个类型的实例。

kotlin 复制代码
val obj: Any = "Hello"

// 类型检查
if (obj is String) {
    // 自动类型转换(智能转换)
    println(obj.length) // obj 自动转换为 String 类型
}

// 否定检查
if (obj !is String) {
    println("不是字符串")
}

智能转换的工作原理

kotlin 复制代码
fun demoSmartCast(obj: Any) {
    if (obj is String) {
        // obj 在此处自动转换为 String
        println("字符串长度:${obj.length}")
        println("字符串:$obj")
    } else if (obj is Int) {
        // obj 在此处自动转换为 Int
        println("整数加 1:${obj + 1}")
    } else if (obj is List<*>) {
        // obj 在此处自动转换为 List<*>
        println("列表大小:${obj.size}")
    } else {
        println("未知类型:${obj.javaClass.simpleName}")
    }
}

// 使用
demoSmartCast("Hello") // 输出:字符串长度:5
demoSmartCast(42) // 输出:整数加 1:43
demoSmartCast(listOf(1, 2, 3)) // 输出:列表大小:3

2.2.2 when 表达式中的智能转换

kotlin 复制代码
fun processValue(value: Any) {
    when (value) {
        is String -> {
            // value 自动转换为 String
            println("字符串:$value,长度:${value.length}")
        }
        is Int -> {
            // value 自动转换为 Int
            println("整数:$value,平方:${value * value}")
        }
        is Double -> {
            // value 自动转换为 Double
            println("浮点数:$value,四舍五入:${value.toInt()}")
        }
        is List<*> -> {
            // value 自动转换为 List<*>
            println("列表,大小:${value.size}")
        }
        else -> {
            println("未知类型")
        }
    }
}

// 使用
processValue("Hello") // 输出:字符串:Hello,长度:5
processValue(42) // 输出:整数:42,平方:1764
processValue(3.14) // 输出:浮点数:3.14,四舍五入:3

2.2.3 as 操作符的安全用法

as 操作符:强制类型转换。as? 只认真实类型,不认值;类型不匹配 → 直接返回 null(is 关键字)

kotlin 复制代码
val obj: Any = "Hello"

// 强制类型转换
val str: String = obj as String // 正确

// 危险的强制转换
val obj2: Any = 42
val str2: String = obj2 as String // 运行时崩溃:ClassCastException

安全类型转换(as?)

  • 如果转换成功,返回目标类型
  • 如果转换失败,返回 null
kotlin 复制代码
val obj: Any = "Hello"

// 安全类型转换
val str: String? = obj as? String // 返回 "Hello"

val obj2: Any = 42
val str2: String? = obj2 as? String // 返回 null

// 与 Elvis 操作符结合
val str3: String = obj as? String ?: "默认值"

实际应用场景

kotlin 复制代码
// 场景1:处理 JSON 解析结果
fun parseJson(json: String): Any {
    // 简化的 JSON 解析
    return if (json.startsWith("{")) {
        mapOf("type" to "object")
    } else {
        json
    }
}

val result = parseJson("{\"name\":\"张三\"}")
if (result is Map<*, *>) {
    println("是对象")
} else {
    println("是字符串")
}

// 场景2:处理 Fragment 参数
fun getBundleArgument(bundle: Bundle?, key: String): String? {
    val value = bundle?.get(key)
    return value as? String // 安全转换为 String
}

2.2.4 智能转换的限制

智能转换生效的条件

  1. val 变量:不可变变量(只读)
  2. 局部变量:局部声明的变量
  3. 自定义 getter 不影响类型:属性的 getter 不改变类型

智能转换不生效的情况

kotlin 复制代码
// 情况1:可变属性(var)
class User(var name: String?)

fun processUser(user: User) {
    if (user.name != null) {
        // 智能转换不生效,因为 name 可能被其他线程修改
        // println(user.name.length) // 编译错误
    }
}

// 解决方式:使用局部变量
fun processUserFixed(user: User) {
    val name = user.name
    if (name != null) {
        println(name.length) // 智能转换生效
    }
}

// 情况2:跨线程访问
var globalValue: String? = null

fun processGlobal() {
    if (globalValue != null) {
        // 智能转换不生效,因为可能被其他线程修改
        // println(globalValue.length) // 编译错误
    }
}

// 解决方式:使用局部变量
fun processGlobalFixed() {
    val value = globalValue
    if (value != null) {
        println(value.length) // 智能转换生效
    }
}

// 情况3:自定义 getter
class Container {
    private var _value: String? = null
    val value: String?
        get() = _value
}

fun processContainer(container: Container) {
    if (container.value != null) {
        // 智能转换生效,因为 getter 不改变类型
        println(container.value.length)
    }
}

2.3 泛型与型变

2.3.1 泛型基础

泛型的价值:编写类型安全的通用代码。

kotlin 复制代码
// 泛型类
class Box<T>(val value: T)

// 使用
val intBox = Box(42) // 推断为 Box<Int>
val stringBox = Box("Hello") // 推断为 Box<String>

// 泛型函数
fun <T> createList(vararg items: T): List<T> {
    return items.toList()
}

// 使用
val intList = createList(1, 2, 3) // List<Int>
val stringList = createList("A", "B", "C") // List<String>
val mixedList = createList(1, "A", 2.0) // List<Any>

泛型约束

kotlin 复制代码
// 约束 T 必须是 Number 的子类
fun <T : Number> sum(a: T, b: T): Double {
    return a.toDouble() + b.toDouble()
}

// 使用
sum(1, 2) // 正确
sum(1.5, 2.5) // 正确
// sum("A", "B") // 编译错误:String 不是 Number 的子类

// 多个约束
fun <T> process(item: T) where T : Number, T : Comparable<T> {
    println(item)
}

2.3.2 in 与 out 关键字

型变的概念:处理泛型类型的子类型关系。

问题引入

kotlin 复制代码
// String 是 CharSequence 的子类
val stringList: List<String> = listOf("A", "B")
val charSequenceList: List<CharSequence> = stringList // 正确

val stringMutableList: MutableList<String> = mutableListOf("A", "B")
// val charSequenceMutableList: MutableList<CharSequence> = stringMutableList // 编译错误

为什么会有这个限制

kotlin 复制代码
// 如果允许将 MutableList<String> 赋值给 MutableList<CharSequence>
val stringList: MutableList<String> = mutableListOf("A", "B")
val charSequenceList: MutableList<CharSequence> = stringList // 假设允许

// 那么就可以添加 Integer
charSequenceList.add(42) // 危险!stringList 中混入了 Integer

// 但实际上 stringList 只能包含 String
for (item in stringList) {
    println(item.length) // 崩溃!item 可能是 Integer
}

out 关键字(协变)

  • 表示泛型类型只作为输出(生产者)
  • 允许将 List<String> 赋值给 List<CharSequence>
kotlin 复制代码
// out 关键字的使用
interface Producer<out T> {
    fun produce(): T
}

class StringProducer : Producer<String> {
    override fun produce(): String {
        return "Hello"
    }
}

// 可以将 Producer<String> 赋值给 Producer<CharSequence>
val stringProducer: Producer<String> = StringProducer()
val charSequenceProducer: Producer<CharSequence> = stringProducer // 正确

in 关键字(逆变)

  • 表示泛型类型只作为输入(消费者)
  • 允许将 MutableList<CharSequence> 赋值给 MutableList<String>
kotlin 复制代码
// in 关键字的使用
interface Consumer<in T> {
    fun consume(item: T)
}

class StringConsumer : Consumer<String> {
    override fun consume(item: String) {
        println("消费:$item")
    }
}

// 可以将 Consumer<CharSequence> 赋值给 Consumer<String>
val charSequenceConsumer: Consumer<CharSequence> = object : Consumer<CharSequence> {
    override fun consume(item: CharSequence) {
        println("消费:$item")
    }
}
val stringConsumer: Consumer<String> = charSequenceConsumer // 正确

// 为什么安全?
stringConsumer.consume("Hello") // String 可以传入 Consumer<CharSequence>

实际应用

kotlin 复制代码
// 场景1:只读列表(out)
fun processList(list: List<out CharSequence>) {
    for (item in list) {
        println(item) // 只能读取
    }
    // list.add("A") // 编译错误:不能添加
}

val stringList = listOf("A", "B")
processList(stringList) // 正确

// 场景2:只写列表(in)
fun fillList(list: MutableList<in String>) {
    list.add("A") // 可以添加 String
    list.add("B") // 可以添加 String
    // val item = list[0] // 编译错误:不能读取(返回的是 Object?)
}

val anyList = mutableListOf<Any>()
fillList(anyList) // 正确

2.3.3 星投影(*)

星投影:当不知道泛型类型时使用。

kotlin 复制代码
// 星投影的使用
fun printList(list: List<*>) {
    for (item in list) {
        println(item)
    }
}

// 使用
val intList = listOf(1, 2, 3)
val stringList = listOf("A", "B", "C")
printList(intList) // 正确
printList(stringList) // 正确

// 限制:不能添加元素
// val list: MutableList<*> = mutableListOf(1, 2, 3)
// list.add(4) // 编译错误

// 正确的使用方式
fun copyList(source: List<*>, destination: MutableList<Any?>) {
    for (item in source) {
        destination.add(item)
    }
}

2.3.4 泛型约束

上界约束

kotlin 复制代码
// 约束 T 必须是 Number 的子类
fun <T : Number> doubleValue(value: T): Double {
    return value.toDouble() * 2
}

// 使用
doubleValue(5) // 正确
doubleValue(3.14) // 正确
// doubleValue("Hello") // 编译错误

多个约束

kotlin 复制代码
// T 必须同时是 Number 和 Comparable
fun <T> max(a: T, b: T): T where T : Number, T : Comparable<T> {
    return if (a > b) a else b
}

// 使用
max(5, 3) // 正确
max(3.14, 2.71) // 正确
// max("A", "B") // 编译错误:String 不是 Number

2.3.5 实际应用场景

场景1:类型安全的缓存

kotlin 复制代码
// 类型安全的缓存
class TypedCache<T : Any> {
    private val cache = mutableMapOf<String, T>()

    fun put(key: String, value: T) {
        cache[key] = value
    }

    fun get(key: String): T? {
        return cache[key]
    }

    fun remove(key: String) {
        cache.remove(key)
    }
}

// 使用
val stringCache = TypedCache<String>()
stringCache.put("name", "张三")
val name = stringCache.get("name") // 返回 String?

val intCache = TypedCache<Int>()
intCache.put("age", 25)
val age = intCache.get("age") // 返回 Int?

场景2:泛型 Repository

kotlin 复制代码
// 泛型 Repository
interface Repository<T> {
    fun getById(id: String): T?
    fun getAll(): List<T>
    fun save(entity: T)
    fun delete(id: String)
}

class InMemoryRepository<T : Any> : Repository<T> {
    private val items = mutableMapOf<String, T>()

    override fun getById(id: String): T? = items[id]

    override fun getAll(): List<T> = items.values.toList()

    override fun save(entity: T) {
        items[entity.hashCode().toString()] = entity
    }

    override fun delete(id: String) {
        items.remove(id)
    }
}

// 使用
data class User(val id: String, val name: String)
data class Product(val id: String, val name: String)

val userRepository = InMemoryRepository<User>()
userRepository.save(User("1", "张三"))
val user = userRepository.getById("1") // 返回 User?

val productRepository = InMemoryRepository<Product>()
productRepository.save(Product("1", "手机"))
val product = productRepository.getById("1") // 返回 Product?

2.4 实战案例:构建类型安全的网络请求封装

2.4.1 需求背景

在 Android 开发中,网络请求是常见需求。我们需要:

  1. 类型安全的请求和响应
  2. 统一的错误处理
  3. 空安全保证
  4. 易于使用和扩展

2.4.2 定义 Result 封装类

kotlin 复制代码
/**
 * 网络请求结果封装类(使用密封类)
 */
sealed class Result<out T> {
    /**
     * 成功结果
     */
    data class Success<out T>(val data: T) : Result<T>()

    /**
     * 错误结果
     */
    data class Error(val exception: Exception) : Result<Nothing>()

    /**
     * 加载中
     */
    object Loading : Result<Nothing>()

    /**
     * 判断是否成功
     */
    val isSuccess: Boolean
        get() = this is Success

    /**
     * 判断是否失败
     */
    val isError: Boolean
        get() = this is Error

    /**
     * 判断是否加载中
     */
    val isLoading: Boolean
        get() = this is Loading

    /**
     * 获取数据,如果失败则返回 null
     */
    fun getOrNull(): T? = when (this) {
        is Success -> data
        else -> null
    }

    /**
     * 获取数据,如果失败则抛出异常
     */
    fun getOrThrow(): T = when (this) {
        is Success -> data
        is Error -> throw exception
        is Loading -> throw IllegalStateException("Result is loading")
    }

    /**
     * 映射数据
     */
    fun <R> map(transform: (T) -> R): Result<R> = when (this) {
        is Success -> Success(transform(data))
        is Error -> Error(exception)
        is Loading -> Loading
    }

    /**
     * 处理结果
     */
    fun onSuccess(action: (T) -> Unit): Result<T> {
        if (this is Success) {
            action(data)
        }
        return this
    }

    fun onError(action: (Exception) -> Unit): Result<T> {
        if (this is Error) {
            action(exception)
        }
        return this
    }

    fun onLoading(action: () -> Unit): Result<T> {
        if (this is Loading) {
            action()
        }
        return this
    }
}

2.4.3 定义网络请求 API

kotlin 复制代码
/**
 * 用户数据类
 */
data class User(
    val id: String,
    val name: String,
    val email: String,
    val age: Int
)

/**
 * API 响应数据类
 */
data class ApiResponse<T>(
    val code: Int,
    val message: String,
    val data: T?
)

/**
 * 用户 API
 */
interface UserApi {
    /**
     * 获取用户信息
     */
    suspend fun getUser(userId: String): Result<User>

    /**
     * 获取用户列表
     */
    suspend fun getUserList(): Result<List<User>>

    /**
     * 创建用户
     */
    suspend fun createUser(user: User): Result<User>

    /**
     * 更新用户
     */
    suspend fun updateUser(userId: String, user: User): Result<User>

    /**
     * 删除用户
     */
    suspend fun deleteUser(userId: String): Result<Unit>
}

2.4.4 实现 API 接口

kotlin 复制代码
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

/**
 * 用户 API 实现
 */
class UserApiImpl : UserApi {
    override suspend fun getUser(userId: String): Result<User> = withContext(Dispatchers.IO) {
        try {
            // 模拟网络请求
            delay(1000)

            // 模拟成功响应
            val user = User(
                id = userId,
                name = "张三",
                email = "zhang@example.com",
                age = 25
            )
            Result.Success(user)

        } catch (e: Exception) {
            Result.Error(e)
        }
    }

    override suspend fun getUserList(): Result<List<User>> = withContext(Dispatchers.IO) {
        try {
            // 模拟网络请求
            delay(1000)

            // 模拟成功响应
            val users = listOf(
                User("1", "张三", "zhang@example.com", 25),
                User("2", "李四", "li@example.com", 30),
                User("3", "王五", "wang@example.com", 28)
            )
            Result.Success(users)

        } catch (e: Exception) {
            Result.Error(e)
        }
    }

    override suspend fun createUser(user: User): Result<User> = withContext(Dispatchers.IO) {
        try {
            // 模拟网络请求
            delay(1000)

            // 模拟成功响应
            Result.Success(user.copy(id = "new_id"))

        } catch (e: Exception) {
            Result.Error(e)
        }
    }

    override suspend fun updateUser(userId: String, user: User): Result<User> = withContext(Dispatchers.IO) {
        try {
            // 模拟网络请求
            delay(1000)

            // 模拟成功响应
            Result.Success(user.copy(id = userId))

        } catch (e: Exception) {
            Result.Error(e)
        }
    }

    override suspend fun deleteUser(userId: String): Result<Unit> = withContext(Dispatchers.IO) {
        try {
            // 模拟网络请求
            delay(1000)

            // 模拟成功响应
            Result.Success(Unit)

        } catch (e: Exception) {
            Result.Error(e)
        }
    }
}

// 模拟 delay 函数
suspend fun delay(milliseconds: Long) {
    kotlinx.coroutines.delay(milliseconds)
}

2.4.5 定义 Repository 层

kotlin 复制代码
/**
 * 用户 Repository
 */
class UserRepository(private val api: UserApi) {

    /**
     * 获取用户信息
     */
    suspend fun getUser(userId: String): Result<User> {
        return api.getUser(userId)
    }

    /**
     * 获取用户列表
     */
    suspend fun getUserList(): Result<List<User>> {
        return api.getUserList()
    }

    /**
     * 创建用户
     */
    suspend fun createUser(user: User): Result<User> {
        return api.createUser(user)
    }

    /**
     * 更新用户
     */
    suspend fun updateUser(userId: String, user: User): Result<User> {
        return api.updateUser(userId, user)
    }

    /**
     * 删除用户
     */
    suspend fun deleteUser(userId: String): Result<Unit> {
        return api.deleteUser(userId)
    }
}

2.4.6 使用示例

kotlin 复制代码
import kotlinx.coroutines.runBlocking

/**
 * 使用示例
 */
fun main() = runBlocking {
    val api = UserApiImpl()
    val repository = UserRepository(api)

    // 示例1:获取用户信息
    println("=== 获取用户信息 ===")
    val result1 = repository.getUser("1")
    result1
        .onSuccess { user ->
            println("用户信息:$user")
        }
        .onError { exception ->
            println("错误:${exception.message}")
        }

    // 示例2:获取用户列表
    println("\n=== 获取用户列表 ===")
    val result2 = repository.getUserList()
    result2
        .onSuccess { users ->
            println("用户列表:")
            users.forEach { user ->
                println("  - $user")
            }
        }
        .onError { exception ->
            println("错误:${exception.message}")
        }

    // 示例3:创建用户
    println("\n=== 创建用户 ===")
    val newUser = User(
        id = "",
        name = "赵六",
        email = "zhao@example.com",
        age = 35
    )
    val result3 = repository.createUser(newUser)
    result3
        .onSuccess { user ->
            println("创建成功:$user")
        }
        .onError { exception ->
            println("错误:${exception.message}")
        }

    // 示例4:使用 map 转换数据
    println("\n=== 使用 map 转换 ===")
    val result4 = repository.getUser("1")
        .map { user ->
            user.copy(age = user.age + 1)
        }
    result4
        .onSuccess { user ->
            println("年龄+1后的用户:$user")
        }

    // 示例5:使用 getOrNull
    println("\n=== 使用 getOrNull ===")
    val user = repository.getUser("1").getOrNull()
    println("用户信息:$user")

    // 示例6:使用 getOrThrow
    println("\n=== 使用 getOrThrow ===")
    try {
        val user2 = repository.getUser("1").getOrThrow()
        println("用户信息:$user2")
    } catch (e: Exception) {
        println("错误:${e.message}")
    }

    // 示例7:处理链式调用
    println("\n=== 链式调用 ===")
    repository.getUser("1")
        .onSuccess { user ->
            println("用户:$user")
        }
        .onError { exception ->
            println("错误:${exception.message}")
        }
        .onLoading {
            println("加载中...")
        }
}

2.4.7 错误处理扩展

kotlin 复制代码
/**
 * 扩展 Result 类,添加更友好的错误处理
 */

/**
 * 获取数据,如果失败则返回默认值
 */
fun <T> Result<T>.getOrDefault(defaultValue: T): T {
    return when (this) {
        is Result.Success -> data
        else -> defaultValue
    }
}

/**
 * 获取数据,如果失败则执行函数返回值
 */
fun <T> Result<T>.getOrElse(onFailure: (Exception) -> T): T {
    return when (this) {
        is Result.Success -> data
        is Result.Error -> onFailure(exception)
        is Result.Loading -> onFailure(IllegalStateException("Result is loading"))
    }
}

/**
 * 获取数据,如果失败则返回 null
 */
fun <T> Result<T>.getOrNull(): T? = when (this) {
    is Result.Success -> data
    else -> null
}

/**
 * 映射成功结果
 */
fun <T, R> Result<T>.map(transform: (T) -> R): Result<R> = when (this) {
    is Result.Success -> Result.Success(transform(data))
    is Result.Error -> Result.Error(exception)
    is Result.Loading -> Result.Loading
}

/**
 * 扁平映射(处理嵌套的 Result)
 */
fun <T, R> Result<T>.flatMap(transform: (T) -> Result<R>): Result<R> = when (this) {
    is Result.Success -> transform(data)
    is Result.Error -> Result.Error(exception)
    is Result.Loading -> Result.Loading
}

/**
 * 过滤结果
 */
fun <T> Result<T>.filter(predicate: (T) -> Boolean): Result<T> = when (this) {
    is Result.Success -> {
        if (predicate(data)) {
            this
        } else {
            Result.Error(IllegalArgumentException("Predicate not satisfied"))
        }
    }
    is Result.Error -> this
    is Result.Loading -> this
}

2.4.8 实际应用:Android ViewModel 集成

kotlin 复制代码
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch

/**
 * 用户 ViewModel
 */
class UserViewModel(
    private val repository: UserRepository
) : ViewModel() {

    /**
     * 用户信息(使用 LiveData 或 StateFlow)
     */
    private val _user = MutableStateFlow<Result<User>?>(null)
    val user: StateFlow<Result<User>?> = _user.asStateFlow()

    /**
     * 用户列表
     */
    private val _userList = MutableStateFlow<Result<List<User>>?>(null)
    val userList: StateFlow<Result<List<User>>?> = _userList.asStateFlow()

    /**
     * 加载用户信息
     */
    fun loadUser(userId: String) {
        viewModelScope.launch {
            _user.value = Result.Loading
            _user.value = repository.getUser(userId)
        }
    }

    /**
     * 加载用户列表
     */
    fun loadUserList() {
        viewModelScope.launch {
            _userList.value = Result.Loading
            _userList.value = repository.getUserList()
        }
    }

    /**
     * 创建用户
     */
    fun createUser(user: User) {
        viewModelScope.launch {
            _user.value = Result.Loading
            _user.value = repository.createUser(user)
        }
    }
}

/**
 * 在 Activity/Fragment 中使用
 */
class UserActivity : AppCompatActivity() {
    private val viewModel: UserViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 观察 ViewModel 的状态
        lifecycleScope.launch {
            viewModel.user.collect { result ->
                when (result) {
                    is Result.Loading -> {
                        // 显示加载中
                        showLoading()
                    }
                    is Result.Success -> {
                        // 显示用户信息
                        hideLoading()
                        showUserInfo(result.data)
                    }
                    is Result.Error -> {
                        // 显示错误
                        hideLoading()
                        showError(result.exception)
                    }
                    null -> {
                        // 初始状态
                    }
                }
            }
        }

        // 加载用户信息
        viewModel.loadUser("1")
    }

    private fun showLoading() {
        // 显示加载中 UI
    }

    private fun hideLoading() {
        // 隐藏加载中 UI
    }

    private fun showUserInfo(user: User) {
        // 显示用户信息
        println("用户:$user")
    }

    private fun showError(exception: Exception) {
        // 显示错误信息
        println("错误:${exception.message}")
    }
}

2.5 本章小结

核心要点回顾

  1. 空安全机制

    • String vs String?:非空类型与可空类型
    • 安全调用(?.):避免 NPE 的优雅方式
    • Elvis 操作符(?:):提供默认值
    • 非空断言(!!):谨慎使用,有风险
  2. 类型转换与智能转换

    • is 操作符:类型检查
    • 智能转换:自动类型转换
    • as? 安全转换:避免 ClassCastException
    • 智能转换的限制:var 属性不生效
  3. 泛型与型变

    • 泛型基础:编写类型安全的通用代码
    • out 协变:生产者模式
    • in 逆变:消费者模式
    • 星投影(*):未知类型处理
    • 泛型约束:限制类型范围
  4. 实战案例

    • 类型安全的 Result 封装类
    • 网络请求 API 设计
    • Repository 层实现
    • ViewModel 集成示例
相关推荐
数字供应链安全产品选型2 小时前
提示词注入、工具滥用、决策黑盒:灵境AIDR如何三步封堵智能体安全三大漏洞?
安全
Lambert_lin02 小时前
Android grade9.0 之后 自定义apk 名称
android·kotlin
刘~浪地球2 小时前
零信任架构设计与实现
安全·架构·安全架构
Kapaseker2 小时前
“点击显示全文” — Compose 实现
android·kotlin
星幻元宇VR3 小时前
VR动感科普单车|让交通安全教育更有参与感
科技·学习·安全·vr·虚拟现实
Chengbei113 小时前
业务视角下的金融SRC快速挖掘思路
网络·安全·web安全·网络安全·金融·系统安全·网络攻击模型
一名优秀的码农3 小时前
vulhub系列-73-RA1NXing Bots(超详细)
安全·web安全·网络安全·网络攻击模型·安全威胁分析
zzb15803 小时前
Kotlin 密封类与延迟初始化学习笔记
笔记·学习·kotlin
朝星3 小时前
Android开发[3]:协程+Flow
android·kotlin