Kotlin - 链式调用处理返回的结果 Result

参考文章

一、概念

public value class Result<out T>  internal constructor( internal val value: Any? ) : Serializable

        对于一些需要关心结果的操作,成功时需要得到数据,失败时需要得到异常信息。返回的 Result 只能是 success 或 failure 中的一种,success持有一个泛型数据T,failure持有一个异常Throwable。

        是一个 value class 意味着是为了安全用来对数据进行包装(装箱)的强类型,返回值是 Serializable 意味着被包装的数据类型需要实现序列化否则抛异常 NotSerializableException。泛型是 <out T> 上限定意味着只能作为生产者,即结果只能被读取。形参类型是 Any? 因为成功传入的是值T失败传入的是异常Throwable,internal 修饰因此我们获取不了。

success( )

failure( )

public inline fun <T> success(value: T): Result<T> = Result(value)

//成功时调用,来包装返回的值。

public inline fun <T> failure(exception: Throwable): Result<T> = Result(createFailure(exception))

//失败时调用,来包装返回的异常。

.isSuccess

.isFailure

public val isSuccess: Boolean get() = value !is Failure

返回的结果是成功吗

public val isFailure: Boolean get() = value is Failure

返回的结果是失败吗

onSuccess( )

onFailure( )

public inline fun <T> Result<T>.onSuccess(action: (value: T) -> Unit): Result<T>

public inline fun <T> Result<T>.onFailure(action: (exception: Throwable) -> Unit): Result<T>

返回的结果是成功或失败时的回调,做一些操作。

getOrNull( )

getOrThrow( )

getOrElse( )

getOrDefault( )

public inline fun getOrNull(): T?

返回的结果是成功就得到值,失败就得到null,不关心异常。

public inline fun <T> Result<T>.getOrThrow(): T

返回的结果是成功就得到值,失败就抛异常,不处理异常。

public inline fun <R, T : R> Result<T>.getOrElse(onFailure: (exception: Throwable) -> R): R

返回的结果是成功就得到值,失败就执行传入的onFailure来处理异常并返回一个值。

public inline fun <R, T : R> Result<T>.getOrDefault(defaultValue: R): R

返回的结果是成功就得到值,失败就得到默认值。

exceptionOrNull( )

public fun exceptionOrNull(): Throwable?

返回的结果是失败就得到异常,成功就得到null,不关心值。

runCatching( )

public inline fun <R> runCatching(block: () -> R): Result<R>

public inline fun <T, R> T.runCatching(block: T.() -> R): Result<R>

执行block,成功就返回值,发生异常就返回失败。

fold( )

public inline fun <R, T> Result<T>.fold(
    onSuccess: (value: T) -> R,
    onFailure: (exception: Throwable) -> R
): R

返回的结果是成功就执行传入的onSuccess将T值转换成R值返回,

返回的结果是失败就执行传入的onFailure来处理异常并返回一个值。

map( )

mapCatching( )

public inline fun <R, T> Result<T>.map(transform: (value: T) -> R): Result<R>

返回的结果是成功就执行传入的transform将T值转换成R值返回,

返回的结果是失败或者执行传入的transform发生异常都返回异常。

public inline fun <R, T> Result<T>.mapCatching(transform: (value: T) -> R): Result<R>

返回的结果是成功就组合执行runCatching(transform),transform将T值转换成R值,执行成功就返回R值,执行异常就返回异常。

返回的结果是失败或者执行传入的transform发生异常都返回异常。

recover( )

recoverCatching( )

public inline fun <R, T : R> Result<T>.recover(transform: (exception: Throwable) -> R): Result<R>

返回的结果是成功就返回值,

返回的结果是失败就执行传入的transform处理异常并返回一个值。

public inline fun <R, T : R> Result<T>.recoverCatching(transform: (exception: Throwable) -> R): Result<R>

返回的结果是成功就返回值,

返回的结果是失败就执行传入的transform处理异常并返回一个值,执行成功就返回值,执行失败就返回异常。

二、基本使用

2.1 封装函数返回值

fun demo(boolean: Boolean): Result<Int> {
    return if (boolean) Result.success(1) else Result.failure(Exception("错误"))
}

val result = demo(false)
    .onSuccess { println(it) }    //不会执行
    .onFailure { println(it.message) }    //打印:错误

println(result.isSuccess)   //打印:false
val recover = result.recover { 5 }
println(recover)    //打印:5

2.2 runCatching()

对可能出现异常的代码使用 runCatching(),会返回一个 Result 对象,通过调用它的 onSuccess() 来处理成功,onFailure() 来处理失败。若要关闭资源,老老实实写 try-catch。

runCatching { 
    //业务代码
}.onSuccess {...}
.onFailure {...}

三、封装 ResultExt.kt

处理两个及以上的 Result 之间关系时,嵌套调用会很麻烦。

一对一依赖方法B依赖方法A的返回值。(更新用户信息依赖上传头像)
一对多依赖方法B和方法C依赖方法A的返回值。(需要看你各个平台账号的视频播放量,这时就需要先获取你的手机号,再通过手机号获取不同平台的播放量,最后相加获得)
多对一依赖方法C依赖于方法A和方法B的返回值。(公司报销出差费用,需要发票单号,也需要获取个人的信息)
选择关系多选一。(手机付款,先用零钱余额支付,如果支付失败(比如余额不足)则使用花呗支付)
Result集合多个 Result 也可以组成一个集合。(上传多张图片到朋友圈,每一张图片都可能上传成功或者失败,这时就获取到了不同图片的 Result 的集合)
//上传头像并获取新头像的url
fun uploadFile(): Result<String> {}
//更新用户信息
fun updateProfile(url: String): Result<Boolean> {}
//展示错误的界面
fun showErrorPage() {}
//更新用户的UI
fun updateProfileUI() {}

uploadFile().onSuccess {
    updateProfile(it).onSuccess {
        updateProfileUI()
    }.onFailure {
        showErrorPage()
    }
}.onFailure {
    showErrorPage()
}

3.1 自定义异常

class ResultExtException(
    message: String = "ResultExtException"
): Exception(message)

3.2 一对一

3.2.1 代码封装

使用 then() 将两个业务串联,任意一个失败都会走 onFailure(),都成功才会走 onSuccess()。还有更多业务需要串联在后面追加 then() 就行。

//内联减少对象的创建
@OptIn(ExperimentalContracts::class)
inline fun <T, R> Result<T>.then(transform: (T) -> Result<R>): Result<R> {
    //Kotlin约定,告诉编译器 transform 最多执行一次
    contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) }
    //成功就从 Result 中获取值传递给下一个业务,失败就返回异常
    if (isSuccess) {
        //这里肯定能获取到值,几种获取值的方法只有getOrNull()合适
        //不选getOrThrow()因为异常正是我们需要的失败结果不能抛
        //不选getOrElse()和getOrDefault()因为业务不同也没办法给默认值
        //因此要做null处理,就返回我们自定义的异常
        val value = getOrNull() ?: return Result.failure(ResultExtException())
        return transform(value)
    } else {
        //作为失败的结果this本身就是Result看似可以直接返回
        //但是this是Result<V>,函数返回值是Result<T>,只能获取异常再包装一下
        //这里肯定能获取异常,但是方法的原因要做null处理,就返回我们自定义的异常
        val exception = exceptionOrNull() ?: return Result.failure(ResultExtException())
        return Result.failure(exception)
    }
}

DeepSeek优化版:

//使用fold()消除null检查复杂性并保持原始语义
//移除契约因为该简单场景编译器能很好优化并减少对实验性API依赖
inline fun <T, R> Result<T>.then(transform: (T) -> Result<R>): Result<R>  = fold(
    onSuccess = { transform(it) },
    onFailure = { Result.failure(it) }
)

3.2.2 使用举例

更新用户信息依赖上传头像。

fun uploadFile(): Result<String> {}
fun updateProfile(url: String): Result<Boolean> {}
fun showErrorPage() {}
fun updateProfileUI() {}

uploadFile()
    .then { updateProfile(it) }
    .onSuccess { updateProfileUI() }
    .onFailure { showErrorPage() }

3.3 一对多

可以直接使用官方的 mapCatching()。

3.3.1 代码封装

当任意一个业务出错时就会执行 onFailure(),当全部业务成功时才会执行 onSuccess()。

//transform的返回值不能定义成Result类型,这里面包含多个业务,用户只负责计算出结果,不应该还要考虑处理异常(包装结果)
inline fun <T, R> Result<T>.dispatch(transform: (T) -> R): Result<R> {
    if (isSuccess) {
        val value = getOrNull() ?: return Result.failure(ResultExtException())
        //runCatching()既做了异常处理,又自动封装到Result中
        return runCatching { transform(value) }
    } else {
        val exception = exceptionOrNull() ?: return Result.failure(ResultExtException())
        return Result.failure(exception)
    }
}

DeepSeek优化版:

inline fun <T, R> Result<T>.dispatch(transform: (T) -> R): Result<R>  = fold(
    onSuccess  = { runCatching { transform(it) } },
    onFailure = { Result.failure(it) }
)

3.3.2 使用举例

需要看你各个平台账号的视频播放量,需要先获取你的手机号,再通过手机号获取不同平台的播放量,最后相加获得。

fun getUserPhoneNumber(): Result<String> {}
fun getBilbilVideoPlayNum(phoneNumber: String): Result<Long> {}
fun getTiktokVideoPlayNum(phoneNumber: String): Result<Long> {}

getUserPhoneNumber().dispatch { phoneNumber ->
    val bilbilVideoPlayNum = getBilbilVideoPlayNum(phoneNumber).getOrThrow()
    val tiktokVideoPlayNum = getTiktokVideoPlayNum(phoneNumber).getOrThrow()
    bilbilVideoPlayNum + tiktokVideoPlayNum
}.onSuccess { println("onSuccess $it") }
.onFailure { println("onFailure $it") }

3.4 多对一

可以直接使用官方的 runCatching() 然后接自定义的一对一 then() 使用。

3.4.1 代码封装

@OptIn(ExperimentalContracts::class)
inline fun <T> resultOf(block: () -> T): Result<T> {
    contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
    return runCatching { block() }
}

DeepSeek优化版:

inline fun <T> resultOf(block: () -> T): Result<T> = runCatching { block() }

3.4.2 使用举例

公司报销出差费用,需要发票单号,也需要获取个人的信息。

fun getUserInfo(): Result<UserInfo> {}
fun getInvoiceNumber(): Result<String> {}
fun reimbursement(userInfo: UserInfo, invoiceNumber: String): Result<Boolean> {}

resultOf {
    val userInfo = getUserInfo().getOrThrow()
    val invoiceNumber = getInvoiceNumber().getOrThrow()
    userInfo to invoiceNumber
}.then {
    reimbursement(it.first, it.second)
}.onFailure { println("onFailure $it") }
.onSuccess { println("onSuccess $it") }

3.5 选择

3.5.1 代码封装

前一个失败才会采用后一个。

fun <T> Result<T>.or(other: Result<T>): Result<T> = if (isSuccess) this else other

3.5.2 使用举例

手机付款,先用零钱余额支付,如果支付失败(比如余额不足)则使用花呗支付。

fun payByChange(): Result<Boolean> {}
fun payByHuabei(): Result<Boolean> {}

payByChange().or(payByHuabei())
    .onFailure { println("onFailure $it") }
    .onSuccess { println("onSuccess $it") }

3.6 集合

3.6.1 代码封装

针对 Result 的集合处理,例如 List<Result<T>>。

//是否全部成功
fun <T> Iterable<Result<T>>.isAllSuccess(): Boolean = all(Result<T>::isSuccess)

//是否全部失败
fun <T> Iterable<Result<T>>.isAllFailure(): Boolean = all(Result<T>::isFailure)

//获取全部成功结果
fun <T> Iterable<Result<T>>.getAllSuccessValues(): List<T> =
    filterTo(mutableListOf()) { it.isSuccess }.map { it.getOrThrow() }

//获取全部失败结果
fun <T> Iterable<Result<T>>.getAllFailureValues(): List<String> =
    filterTo(mutableListOf()) { it.isFailure }.map {
        (it.exceptionOrNull() ?: Throwable("")).message.toString()
    }

3.6.2 使用举例

上传多张图片到朋友圈,每一张图片都可能上传成功或者失败,这时就获取到了不同图片的 Result 的集合。

fun main() {
    val successList = listOf(
        Result.success(1),
        Result.success(2),
        Result.success(3)
    )
    val failureList = listOf<Result<Int>>(
        Result.failure(Throwable("哈")),
        Result.failure(Throwable("呵"))
    )
    val list = listOf(
        Result.success(1),
        Result.failure(Throwable("哈")),
        Result.success(3),
        Result.failure(Throwable("呵"))
    )
    println(successList.isAllSuccess()) //打印:true
    println(failureList.isAllFailure()) //打印:true
    println(list.isAllSuccess())  //打印:false
    println(list.isAllFailure())  //打印:false
    list.getAllSuccessValues().forEach { print("$it,") }  //打印:1,3
    list.getAllFailureValues().forEach { print("$it,") }    //打印:哈,呵
}

3.7 以上汇总成文件

/**
 * 一对一关系:后一个业务依赖前一个业务的结果。
 */
inline fun <T, R> Result<T>.then(transform: (T) -> Result<R>): Result<R>  = fold(
    onSuccess = { transform(it) },
    onFailure = { Result.failure(it) }
)

/**
 * 一对多:多个业务依赖同一个业务的结果。
 */
inline fun <T, R> Result<T>.dispatch(transform: (T) -> R): Result<R>  = fold(
    onSuccess  = { runCatching { transform(it) } },
    onFailure = { Result.failure(it) }
)

/**
 * 多对一:后一个业务依赖多个业务合并的结果。
 */
inline fun <T> resultOf(block: () -> T): Result<T> = runCatching { block() }

/**
 * 选择:前一个失败才会采用后一个。
 */
fun <T> Result<T>.or(other: Result<T>): Result<T> = if (isSuccess) this else other

/**
 * 是否全部成功:集合中的Result是否全是success。
 */
fun <T> Iterable<Result<T>>.isAllSuccess(): Boolean = all(Result<T>::isSuccess)

/**
 * 是否全部失败:集合中的Result是否全是failure。
 */
fun <T> Iterable<Result<T>>.isAllFailure(): Boolean = all(Result<T>::isFailure)

/**
 * 获取全部成功结果:将集合中所有success的Result的值汇总到一个List中。
 */
fun <T> Iterable<Result<T>>.getAllSuccessValues(): List<T> =
    filterTo(mutableListOf()) { it.isSuccess }.map { it.getOrThrow() }

/**
 * 获取全部失败结果:将集合中所有failure的Result的异常汇总到一个List中。
 */
fun <T> Iterable<Result<T>>.getAllFailureValues(): List<String> =
    filterTo(mutableListOf()) { it.isFailure }.map {
        (it.exceptionOrNull() ?: Throwable("")).message.toString()
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值