第 11 课2026.07.27
网络请求不仅会成功,也可能出现断网、HTTP 状态异常或数据解析失败。共享层可以用密封类型把结果完整交给调用方,让 Android 和 iOS 对所有情况进行明确处理,而不是用空对象或 UI 占位状态掩盖错误。
第 11 课:用密封类型表达数据结果
- 日期:2026-07-27
- 课程序号:11
- 知识点:使用
sealed interface明确表达成功与失败
用途
网络请求不仅会成功,也可能出现断网、HTTP 状态异常或数据解析失败。共享层可以用密封类型把结果完整交给调用方,让 Android 和 iOS 对所有情况进行明确处理,而不是用空对象或 UI 占位状态掩盖错误。
核心概念
- 密封类型限制了所有合法分支,
when可以穷尽处理。 - 成功分支携带真实业务数据。
- 失败分支携带数据层识别出的错误类型。
- 错误应在发生它的数据层归类,UI 只决定如何展示。
- 协程取消不是普通业务失败,不应转换成错误页面状态。
示例代码
定义共享结果与错误类型:
kotlin
sealed interface DataError {
data object Network : DataError
data class Http(val statusCode: Int) : DataError
data object InvalidPayload : DataError
}
sealed interface DataResult<out T> {
data class Success<T>(val value: T) : DataResult<T>
data class Failure(val error: DataError) : DataResult<Nothing>
}Repository 接收已经由网络层归类的结果,并映射成功数据:
kotlin
interface UserRemoteDataSource {
suspend fun fetchUser(id: Long): DataResult<UserDto>
}
class UserRepository(
private val remote: UserRemoteDataSource,
) {
suspend fun loadUser(id: Long): DataResult<User> {
return when (val result = remote.fetchUser(id)) {
is DataResult.Success -> {
val dto = result.value
DataResult.Success(
User(
id = dto.id,
displayName = dto.name,
),
)
}
is DataResult.Failure -> result
}
}
}调用方必须明确处理两个结果分支:
kotlin
when (val result = repository.loadUser(1L)) {
is DataResult.Success -> println(result.value.displayName)
is DataResult.Failure -> println("加载失败:${result.error}")
}网络异常、HTTP 状态和解析异常应由 UserRemoteDataSource 根据实际响应归类,Repository 不猜测错误来源。
5 分钟练习
给 DataError 增加未登录错误 Unauthorized,并在调用方的失败分支中单独识别它。
参考答案
kotlin
sealed interface DataError {
data object Network : DataError
data class Http(val statusCode: Int) : DataError
data object InvalidPayload : DataError
data object Unauthorized : DataError
}
when (val result = repository.loadUser(1L)) {
is DataResult.Success -> println(result.value.displayName)
is DataResult.Failure -> when (result.error) {
DataError.Unauthorized -> println("请先登录")
else -> println("加载失败:${result.error}")
}
}真实项目中,Unauthorized 应来自明确的鉴权协议或 HTTP 状态映射,不能由 UI 根据空数据自行推断。
与上一课的联系
上一课的 Repository 只展示了成功链路;本课补齐失败语义,使“数据源 → Repository → UI”的每一种结果都有明确类型和责任边界。