第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:非空类型,不能为 nullString?:可空类型,可以为 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 智能转换的限制
智能转换生效的条件:
- val 变量:不可变变量(只读)
- 局部变量:局部声明的变量
- 自定义 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 开发中,网络请求是常见需求。我们需要:
- 类型安全的请求和响应
- 统一的错误处理
- 空安全保证
- 易于使用和扩展
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 本章小结
核心要点回顾
-
空安全机制
StringvsString?:非空类型与可空类型- 安全调用(?.):避免 NPE 的优雅方式
- Elvis 操作符(?:):提供默认值
- 非空断言(!!):谨慎使用,有风险
-
类型转换与智能转换
is操作符:类型检查- 智能转换:自动类型转换
as?安全转换:避免 ClassCastException- 智能转换的限制:var 属性不生效
-
泛型与型变
- 泛型基础:编写类型安全的通用代码
out协变:生产者模式in逆变:消费者模式- 星投影(*):未知类型处理
- 泛型约束:限制类型范围
-
实战案例
- 类型安全的 Result 封装类
- 网络请求 API 设计
- Repository 层实现
- ViewModel 集成示例