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 不再被类型擦除蒙蔽双眼
相关推荐
sweetying2 小时前
30了,人生按部就班
android·程序员
用户2018792831672 小时前
Binder驱动缓冲区的工作机制答疑
android
真夜2 小时前
关于rngh手势与Slider组件手势与事件冲突解决问题记录
android·javascript·app
用户2018792831672 小时前
浅析Binder通信的三种调用方式
android
用户093 小时前
深入了解 Android 16KB内存页面
android·kotlin
小孔龙3 小时前
03.Kotlin Serialization - 认识序列化器
kotlin·json
火车叼位4 小时前
Android Studio与命令行Gradle表现不一致问题分析
android
前行的小黑炭6 小时前
【Android】 Context使用不当,存在内存泄漏,语言不生效等等
android·kotlin·app
前行的小黑炭6 小时前
【Android】CoordinatorLayout详解;实现一个交互动画的效果(上滑隐藏,下滑出现);附例子
android·kotlin·app
用户20187928316718 小时前
Android黑夜白天模式切换原理分析
android