一、概念
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。
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将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 {...}
三、封装
3.1 问题举例
处理两个及以上的 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.2 ResultExt.kt 具体封装文件
一对一依赖 | 方法B依赖方法A的返回值。(更新用户信息依赖上传头像) |
一对多依赖 | 方法B和方法C依赖方法A的返回值。(需要看你各个平台账号的视频播放量,这时就需要先获取你的手机号,再通过手机号获取不同平台的播放量,最后相加获得) |
多对一依赖 | 方法C依赖于方法A和方法B的返回值。(公司报销出差费用,需要发票单号,也需要获取个人的信息) |
选择关系 | 多选一。(手机付款,先用零钱余额支付,如果支付失败(比如余额不足)则使用花呗支付) |
Result集合 | 多个 Result 也可以组成一个集合。(上传多张图片到朋友圈,每一张图片都可能上传成功或者失败,这时就获取到了不同图片的 Result 的集合) |
class ResultException(
message: String = "inner value error"
): Exception(message)
//一对一
@OptIn(ExperimentalContracts::class)
inline fun <R, T> Result<R>.then(transform: (R) -> Result<T>): Result<T> {
contract {
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
if (isSuccess) {
val value = getOrNull() ?: return Result.failure(ResultException())
return transform(value)
} else {
val exception = exceptionOrNull() ?: return Result.failure(ResultException())
return Result.failure(exception)
}
}
//一对多
@OptIn(ExperimentalContracts::class)
inline fun <R, T> Result<R>.dispatch(transform: (R) -> T): Result<T> {
contract {
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
if (isSuccess) {
val value = getOrNull() ?: return Result.failure(ResultException())
return runCatching {
transform(value)
}
} else {
val exception = exceptionOrNull() ?: return Result.failure(ResultException())
return Result.failure(exception)
}
}
//多对一
@OptIn(ExperimentalContracts::class)
inline fun <T> zip(block: () -> T): Result<T> {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return runCatching {
block()
}
}
//选择
fun <T> Result<T>.or(result: Result<T>): Result<T> {
return when {
isSuccess -> this
else -> result
}
}
//集合
3.3 使用举例
3.3.1 一对一
更新用户信息依赖上传头像。
fun uploadFile(): Result<String> {}
fun updateProfile(url: String): Result<Boolean> {}
fun showErrorPage() {}
fun updateProfileUI() {}
uploadFile()
.andThen { updateProfile(it) }
.onSuccess { updateProfileUI() }
.onFailure { showErrorPage() }
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.3.3 多对一
公司报销出差费用,需要发票单号,也需要获取个人的信息。
fun getUserInfo(): Result<UserInfo> {}
fun getInvoiceNumber(): Result<String> {}
fun reimbursement(userInfo: UserInfo, invoiceNumber: String): Result<Boolean> {}
zip {
val userInfo = getUserInfo().getOrThrow()
val invoiceNumber = getInvoiceNumber().getOrThrow()
userInfo to invoiceNumber
}.andThen {
reimbursement(it.first, it.second)
}.onFailure { println("onFailure $it") }
.onSuccess { println("onSuccess $it") }
3.3.4 选择
手机付款,先用零钱余额支付,如果支付失败(比如余额不足)则使用花呗支付。
fun payByChange(): Result<Boolean> {}
fun payByHuabei(): Result<Boolean> {}
payByChange().or(payByHuabei())
.onFailure { println("onFailure $it") }
.onSuccess { println("onSuccess $it") }
3.3.5 集合
上传多张图片到朋友圈,每一张图片都可能上传成功或者失败,这时就获取到了不同图片的 Result 的集合。
fun uploadFiles(paths: List<String>): List<Result<String>> {}
//哪些图片上传成功了
fun <V, R : Result<V>> valuesOf(results: List<R>): List<V> {
return results.asIterable().filterValues()
}
fun <V> Iterable<Result<V>>.filterValues(): List<V> {
return filterValuesTo(ArrayList())
}
fun <V, C : MutableCollection<in V>> Iterable<Result<V>>.filterValuesTo(destination: C): C {
for (element in this) {
if (element.isSuccess) {
val value = element.getOrNull() ?: continue
destination.add(value)
}
}
return destination
}
//是否全部上传成功
fun <V> Iterable<Result<V>>.allSuccess(): Boolean {
return all(Result<V>::isSuccess)
}