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 不再被类型擦除蒙蔽双眼
相关推荐
万能的小裴同学1 小时前
Android M3U8视频播放器
android·音视频
q***57741 小时前
MySql的慢查询(慢日志)
android·mysql·adb
JavaNoober2 小时前
Android 前台服务 "Bad Notification" 崩溃机制分析文档
android
城东米粉儿2 小时前
关于ObjectAnimator
android
zhangphil3 小时前
Android渲染线程Render Thread的RenderNode与DisplayList,引用Bitmap及Open GL纹理上传GPU
android
FinClip4 小时前
政务App如何真正成为便民好帮手?
app
火柴就是我4 小时前
从头写一个自己的app
android·前端·flutter
饕餮争锋5 小时前
Kotlin: [Internal Error] java.lang.NoSuchFieldError: FILE_HASHING_STRATEGY
java·kotlin
lichong9515 小时前
XLog debug 开启打印日志,release 关闭打印日志
android·java·前端
用户69371750013846 小时前
14.Kotlin 类:类的形态(一):抽象类 (Abstract Class)
android·后端·kotlin