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())
}
七、总结
-
依赖关系:
- 一对一:使用
flatMap
或map
链式调用 - 一对多:使用
runCatching
组合多个操作 - 多对一:收集多个独立操作的结果
- 一对一:使用
-
选择关系:
- 使用
recover
或recoverCatching
提供备用方案 - 实现
firstSuccess
选择第一个成功的操作
- 使用
-
集合处理:
sequence
转换List<Result<T>>
为Result<List<T>>
- 分区处理成功和失败的结果
-
协程支持:
- 在挂起函数中使用
Result.runCatching
- 并行执行多个异步操作并组合结果
- 在挂起函数中使用
-
扩展函数:
- 创建实用扩展函数使
Result
处理更简洁
- 创建实用扩展函数使
这些模式可以帮助你在Kotlin中构建健壮的错误处理流程,特别是在复杂的异步或依赖操作场景中。
更多分享
- 一文吃透Kotlin中冷流(Clod Flow)和热流(Hot Flow)
- 一文带你吃透Kotlin协程的launch()和async()的区别
- Kotlin 委托与扩展函数------新手入门
- Kotlin 作用域函数(let、run、with、apply、also)的使用指南
- 一文带你吃透Kotlin中 lateinit 和 by lazy 的区别和用法
- Kotlin 扩展方法(Extension Functions)使用详解
- Kotlin 中 == 和 === 的区别
- Kotlin 操作符与集合/数组方法详解------新手指南
- Kotlin 中 reified 配合 inline 不再被类型擦除蒙蔽双眼