Kotlin Result 类型扩展详解 —— 新手使用指南

Result 是 Kotlin 标准库中用于封装成功或失败结果的类型,特别适合函数式错误处理。本文将详细扩展 Result 的各种使用场景,包括依赖关系、选择关系和对协程的支持。

一、基本 Result 使用

首先回顾基本用法:

kotlin 复制代码
fun divide(a: Int, b: Int): Result<Int> {
    return if (b == 0) {
        Result.failure(ArithmeticException("Division by zero"))
    } else {
        Result.success(a / b)
    }
}

fun main() {
    val result = divide(10, 2)
    result.onSuccess { println("Result: $it") }
          .onFailure { println("Error: ${it.message}") }
}

二、依赖关系处理

2.1 一对一依赖(线性依赖)

kotlin 复制代码
// 一个操作依赖于前一个操作的结果
fun processUserData(userId: String): Result<String> {
    return fetchUser(userId)
        .flatMap { user -> validateUser(user) }
        .map { validUser -> formatUserData(validUser) }
}

private fun fetchUser(id: String): Result<User> {
    return if (id.isNotEmpty()) Result.success(User(id, "John")) 
           else Result.failure(IllegalArgumentException("Invalid ID"))
}

private fun validateUser(user: User): Result<User> {
    return if (user.name.isNotEmpty()) Result.success(user)
           else Result.failure(IllegalArgumentException("Invalid user"))
}

private fun formatUserData(user: User): String {
    return "User: ${user.name} (ID: ${user.id})"
}

data class User(val id: String, val name: String)

fun main() {
    processUserData("123").fold(
        onSuccess = { println(it) },
        onFailure = { println("Error: ${it.message}") }
    )
}

2.2 一对多依赖(一个操作依赖多个独立操作)

kotlin 复制代码
// 多个独立操作,全部成功才能继续
fun processOrder(orderId: String, userId: String): Result<OrderResult> {
    return Result.runCatching {
        val user = fetchUser(userId).getOrThrow()
        val order = fetchOrder(orderId).getOrThrow()
        val inventory = checkInventory(order.items).getOrThrow()
        
        OrderResult(user, order, inventory)
    }
}

// 模拟其他函数
private fun fetchOrder(id: String): Result<Order> = Result.success(Order(id, listOf("item1", "item2")))
private fun checkInventory(items: List<String>): Result<Boolean> = Result.success(true)

data class Order(val id: String, val items: List<String>)
data class OrderResult(val user: User, val order: Order, val hasInventory: Boolean)

fun main() {
    processOrder("order123", "user123").fold(
        onSuccess = { println("Order processed: $it") },
        onFailure = { println("Failed to process order: ${it.message}") }
    )
}

2.3 多对一依赖(多个操作可以独立失败)

kotlin 复制代码
// 多个操作,收集所有结果(成功或失败)
fun collectMultipleResults(): Result<List<Result<Any>>> {
    val results = listOf(
        fetchUser("1"),
        fetchProduct("p1"),
        fetchSettings("s1")
    )
    return Result.success(results)
}

// 或者只关心成功的部分
fun collectSuccessfulResults(): List<Any> {
    return listOf(
        fetchUser("1"),
        fetchProduct("p1"),
        fetchSettings("s1")
    ).filter { it.isSuccess }.map { it.getOrNull()!! }
}

// 模拟函数
private fun fetchProduct(id: String): Result<Product> = Result.success(Product(id))
private fun fetchSettings(id: String): Result<Settings> = Result.failure(IllegalStateException("Settings unavailable"))

data class Product(val id: String)
data class Settings(val config: String)

fun main() {
    println("All results:")
    collectMultipleResults().getOrThrow().forEach { println(it) }
    
    println("\nSuccessful results:")
    println(collectSuccessfulResults())
}

三、选择关系(替代方案)

kotlin 复制代码
// 尝试多个操作,使用第一个成功的
fun findAlternativeResource(): Result<String> {
    return fetchPrimaryResource()
        .recoverCatching { fetchSecondaryResource().getOrThrow() }
        .recoverCatching { fetchFallbackResource().getOrThrow() }
}

// 或者更简洁的写法
fun findAlternativeResource2(): Result<String> {
    return listOf(
        { fetchPrimaryResource() },
        { fetchSecondaryResource() },
        { fetchFallbackResource() }
    ).firstSuccess()
}

// 扩展函数:获取第一个成功的Result
fun <T> List<() -> Result<T>>.firstSuccess(): Result<T> {
    for (operation in this) {
        operation().onSuccess { return Result.success(it) }
    }
    return Result.failure(IllegalStateException("All operations failed"))
}

// 模拟函数
private fun fetchPrimaryResource(): Result<String> = Result.failure(IOException("Primary unavailable"))
private fun fetchSecondaryResource(): Result<String> = Result.failure(IOException("Secondary unavailable"))
private fun fetchFallbackResource(): Result<String> = Result.success("Fallback data")

fun main() {
    findAlternativeResource().fold(
        onSuccess = { println("Resource: $it") },
        onFailure = { println("All attempts failed: ${it.message}") }
    )
    
    findAlternativeResource2().fold(
        onSuccess = { println("Alternative resource: $it") },
        onFailure = { println("All alternatives failed: ${it.message}") }
    )
}

四、Result 集合处理

kotlin 复制代码
// 处理Result集合的几种方式

// 1. 全部成功才成功
fun processAllOrFail(results: List<Result<Int>>): Result<List<Int>> {
    return results.fold(Result.success(emptyList())) { acc, result ->
        acc.flatMap { list -> result.map { list + it } }
    }
}

// 2. 收集所有结果(成功和失败分开)
data class PartitionedResults<T>(val successes: List<T>, val failures: List<Throwable>)

fun <T> partitionResults(results: List<Result<T>>): PartitionedResults<T> {
    return results.fold(PartitionedResults(emptyList(), emptyList())) { acc, result ->
        result.fold(
            onSuccess = { acc.copy(successes = acc.successes + it) },
            onFailure = { acc.copy(failures = acc.failures + it) }
        )
    }
}

// 3. 只收集成功的
fun <T> getAllSuccesses(results: List<Result<T>>): List<T> {
    return results.mapNotNull { it.getOrNull() }
}

fun main() {
    val results = listOf(
        Result.success(1),
        Result.failure(RuntimeException("Error 1")),
        Result.success(2),
        Result.failure(IllegalStateException("Error 2")),
        Result.success(3)
    )
    
    println("All or nothing:")
    println(processAllOrFail(results))
    
    println("\nPartitioned:")
    println(partitionResults(results))
    
    println("\nOnly successes:")
    println(getAllSuccesses(results))
}

五、协程支持

kotlin 复制代码
import kotlinx.coroutines.*
import java.io.IOException

// 协程中的Result使用
suspend fun fetchUserData(userId: String): Result<String> = withContext(Dispatchers.IO) {
    Result.runCatching {
        // 模拟网络请求
        delay(1000)
        if (userId == "error") throw IOException("Network error")
        "Data for $userId"
    }
}

suspend fun fetchUserProfile(userId: String): Result<String> = withContext(Dispatchers.IO) {
    Result.runCatching {
        delay(500)
        if (userId == "invalid") throw IllegalStateException("Invalid user")
        "Profile of $userId"
    }
}

// 并行获取多个数据
suspend fun fetchMultipleData(userId: String): Result<Pair<String, String>> = coroutineScope {
    val deferredData = async { fetchUserData(userId) }
    val deferredProfile = async { fetchUserProfile(userId) }
    
    val dataResult = deferredData.await()
    val profileResult = deferredProfile.await()
    
    if (dataResult.isSuccess && profileResult.isSuccess) {
        Result.success(Pair(dataResult.getOrThrow(), profileResult.getOrThrow()))
    } else {
        val exceptions = listOfNotNull(
            dataResult.exceptionOrNull(),
            profileResult.exceptionOrNull()
        )
        Result.failure(exceptions.firstOrNull() ?: Exception("Unknown error"))
    }
}

// 使用recoverWith处理异步错误
suspend fun fetchWithFallback(userId: String): Result<String> {
    return fetchUserData(userId)
        .recoverCatching { fetchUserProfile(userId).getOrThrow() }
}

fun main() = runBlocking {
    println("Successful case:")
    fetchMultipleData("123").fold(
        onSuccess = { println(it) },
        onFailure = { println("Error: ${it.message}") }
    )
    
    println("\nError case:")
    fetchMultipleData("error").fold(
        onSuccess = { println(it) },
        onFailure = { println("Error: ${it.message}") }
    )
    
    println("\nRecovery case:")
    fetchWithFallback("error").fold(
        onSuccess = { println("Fetched: $it") },
        onFailure = { println("Failed: ${it.message}") }
    )
}

六、实用扩展函数

kotlin 复制代码
// 一些有用的Result扩展函数

// 将多个Result合并为一个
fun <T1, T2> Result<T1>.combine(other: Result<T2>): Result<Pair<T1, T2>> {
    return this.flatMap { t1 -> other.map { t2 -> t1 to t2 } }
}

// 将List<Result<T>>转换为Result<List<T>>
fun <T> List<Result<T>>.sequence(): Result<List<T>> {
    return this.fold(Result.success(emptyList())) { acc, result ->
        acc.flatMap { list -> result.map { list + it } }
    }
}

// 过滤成功的Result
fun <T> List<Result<T>>.filterSuccess(): List<T> {
    return this.mapNotNull { it.getOrNull() }
}

// 执行副作用并返回原始Result
fun <T> Result<T>.onSuccess(action: (T) -> Unit): Result<T> {
    this.onSuccess { action(it) }
    return this
}

fun <T> Result<T>.onFailure(action: (Throwable) -> Unit): Result<T> {
    this.onFailure { action(it) }
    return this
}

fun main() {
    val r1 = Result.success(1)
    val r2 = Result.success("two")
    val r3 = Result.failure<Boolean>(RuntimeException("Error"))
    
    println("Combine:")
    println(r1.combine(r2))
    println(r1.combine(r3))
    
    println("\nSequence:")
    val results = listOf(r1, r2.map { it.length }, Result.success(3.0))
    println(results.sequence())
    
    println("\nFilter success:")
    val mixedResults = listOf(r1, r2, r3)
    println(mixedResults.filterSuccess())
}

七、总结

  1. 依赖关系

    • 一对一:使用 flatMapmap 链式调用
    • 一对多:使用 runCatching 组合多个操作
    • 多对一:收集多个独立操作的结果
  2. 选择关系

    • 使用 recoverrecoverCatching 提供备用方案
    • 实现 firstSuccess 选择第一个成功的操作
  3. 集合处理

    • sequence 转换 List<Result<T>>Result<List<T>>
    • 分区处理成功和失败的结果
  4. 协程支持

    • 在挂起函数中使用 Result.runCatching
    • 并行执行多个异步操作并组合结果
  5. 扩展函数

    • 创建实用扩展函数使 Result 处理更简洁

这些模式可以帮助你在Kotlin中构建健壮的错误处理流程,特别是在复杂的异步或依赖操作场景中。

更多分享

  1. 一文吃透Kotlin中冷流(Clod Flow)和热流(Hot Flow)
  2. 一文带你吃透Kotlin协程的launch()和async()的区别
  3. Kotlin 委托与扩展函数------新手入门
  4. Kotlin 作用域函数(let、run、with、apply、also)的使用指南
  5. 一文带你吃透Kotlin中 lateinit 和 by lazy 的区别和用法
  6. Kotlin 扩展方法(Extension Functions)使用详解
  7. Kotlin 中 == 和 === 的区别
  8. Kotlin 操作符与集合/数组方法详解------新手指南
  9. Kotlin 中 reified 配合 inline 不再被类型擦除蒙蔽双眼
相关推荐
stevenzqzq2 小时前
app性能日志工具
android
DB哥讲数据库3 小时前
【最新】MySQL9.7安装教程:图文详解(附MySQL安装包)
android·mysql·adb
IT乐手3 小时前
Android 写本地日志工具类
android·java
非典型Android程序员4 小时前
Launcher学习总结
android
Coffeeee4 小时前
Android功耗优化,哪些代码会让你的机子发热又耗电
android·架构·kotlin
Android打工仔4 小时前
Kotlin 编译器是如何把 suspend 函数变成状态机的?
kotlin
Android打工仔4 小时前
一个 suspend 函数,为什么会变成一个普通函数?
kotlin
xianrenli386 小时前
Android14自动时区更新
android·学习·framework·aosp14·自动时区
心中有国也有家6 小时前
AtomGit Flutter 鸿蒙客户端:零外部资源的应用打包策略
android·javascript·flutter·华为·harmonyos
-SOLO-6 小时前
解决gralde下载太慢的问题
android