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 不再被类型擦除蒙蔽双眼
相关推荐
IT技术图谱27 分钟前
【绝非标题党】Android15适配,太恶心了
android·面试
harry235day29 分钟前
Compose 自定义转盘
kotlin·android jetpack
古鸽1008630 分钟前
Vehicle HAL 介绍
android
和煦的春风31 分钟前
Linux | 关于CPU 调频的一些QA
android·linux
dafaycoding34 分钟前
使用 Perlin Noise、Catmull-Rom 创建闭合平滑曲线
android·计算机视觉
行墨35 分钟前
RePlugin 插件化工程中插件与宿主之间的通信方式
android
V少年36 分钟前
深入浅出安卓SurfaceFlinger
android
V少年36 分钟前
深入浅出安卓MediaCodec
android
_一条咸鱼_1 小时前
大厂Android面试秘籍: Android Activity 状态保存与恢复机制(六)
android·面试·kotlin
_一条咸鱼_1 小时前
大厂Android面试秘籍:Activity 布局加载与视图管理(五)
android·面试·kotlin