巧用Kotlin Result 类

代码的可读性是编程时的至关重要的一环。实现可读性的一个重要部分在于你如何命名对象、类和变量。它应该是直接明了的,即使这意味着使用额外的字词。别担心,向变量名添加额外的信息不会在你的代码库中造成混乱。

我们都知道,计算机只能理解两件事:1和0,或者说开和关。同样,我们的人类思维也很相似。通过将某物命名为真或假,我们更容易理解它。这有助于我们的大脑更容易地记住或处理那些信息。例如,回想一下我们童年时期在考试中的简单真假题。读一下陈述,给出答案。

让我们探索一下Kotlin的Result类如何应用同样的理论,使代码更容易阅读。

在大多数情况下,当需要检查代码中的操作是否成功时,使用这个函数:

val number = 10
val result = if (number > 5) {
    "Number is greater than 5"
} else {
    "Number is not greater than 5"
}

这是一个非常基本的操作示例,用于检查条件是否为真或假。然而,在这种情况下,已经知道了else部分,这给了我们为其他情况编写代码。它为用户或开发者提供了信息,说明这将是两种情况的结果。

如果遇到稍微复杂一些的条件,需要使用其他工具来处理情况,比如try-catch块呢?我们不会深入探讨tryCatch块,但会看到一个try-catch块是如何工作的。

val input = "abc"
val number = try {
    input.toInt() // Try to parse the string to an integer
} catch (e: NumberFormatException) {
    null // If an exception occurs, assign null to 'number'
}
val result = if (number != null) {
    "Parsed number: $number"
} else {
    "Could not parse input as a number"
}
println(result)

探索一下上面的代码:有一个输入,它是一个字符串,我们试图将它转换成一个整数,这是不可能的。所以,它会抛出一个异常,将会在代码的catch块中捕获这个异常。然后,将根据这个异常显示一个消息。本质上,try-catch块被用来在进行操作时处理异常,这样我们就可以按照期望的方式管理它们。

现在已经理解了何时以及如何使用try-catch块,我认为每次执行一个操作时都写一个try-catch块对于可读性来说并不好。它不符合不要重复自己的原则。

让我们用一个真实的例子来改进上面的代码。考虑一下在我们的代码中常常在哪里使用try-catch块。是的,你猜对了——当执行API调用时。对于每一个API调用,都使用一个try-catch块。

import io.ktor.client.*
import io.ktor.client.features.json.*
import io.ktor.client.features.json.serializer.*
import io.ktor.client.request.*
import io.ktor.http.*

val httpClient = HttpClient {
    install(JsonFeature) {
        serializer = KotlinxSerializer()
    }
}

suspend fun performApiCall(): String? {
    return try {
        val response=httpClient.get("https://your-api-url.com/endpoint").body<String>()
        response // Return response if successful
    } catch (e: Exception) {
        e.printStackTrace() // Handle or log the exception here
        null // Return null in case of an error
    }
}

如你所见,在这里使用的是KtorClient,但无论是Retrofit还是其他类似的工具,概念都是一样的。

在上面的代码片段中,创建了一个Ktor客户端,并使用GET操作在try-catch块中进行了API调用。想象一下在你的业务逻辑中写50个API调用;即使它会运行,但每次你写一个API调用时,try-catch块都会被重复。

将使用runCatching来做这个修改。runCatching使用了一个try-catch块,但它还添加了另一样东西:Result类。这里的R是泛型,代表String。

return runCatching {
        httpClient.get("https://your-api-url.com/endpoint").body<String>()
    }
public inline fun <R> runCatching(block: () -> R): Result<R> {
    return try {
        Result.success(block())
    } catch (e: Throwable) {
        Result.failure(e)
    }
}

现在,通过在代码中使用runCatching,它已经看起来超级干净了。让我们利用Result类提供的另一个函数,进一步提升其清晰度和可读性。

由于结果可能是Success或Failure,可以使用一个超级干净的方法来轻松处理错误。只需替换几个东西,Result类的魔力就会让你的代码闪闪发光。

suspend fun performApiCall(): Result<String> {
        return runCatching {
            httpClient.get("https://your-api-url.com/endpoint").body<String>()
        }
    }
class MyViewModel : ViewModel() {
    fun fetchData() {
        viewModelScope.launch {
            val result = repository.performApiCall()
            
            result.onSuccess {
                // Handle success if needed
            }.onFailure {
                // Handle failure if needed
            }
        }
    }
}

Result类并不仅限于上述例子;它还提供了许多更多的使用场景,配合不同的扩展函数。以下是一些例子:

//For cases solely interested in success response, using getOrNull() function and handling errors manually:
viewModelScope.launch {
    if (result.isSuccess) {
        val data = result.getOrNull()
        // handle data
    } else {
        // handle error
    }
}

//Folding the response, a concise approach for clean and readable code:
viewModelScope.launch {
    result.fold(onSuccess = { data ->
        // handle data
    }, onFailure = { error ->
        // handle error
    })
}

我们仍然需要每次都写'runCathing',但可以通过使用扩展来进一步改进这一点。以下,可以增强我们的Ktor客户端。可以创建你需要的所有扩展,如GetResult,PutResult,DeleteResult...., 来解决这个问题。可以参考这个KtorBoost的库。库的链接:KtorBoost https://github.com/AndroidPoet/KtorBoost

// create this extenstiion function in your code base 
suspend inline fun <reified R> HttpClient.getResult(
   urlString: String,
   builder: HttpRequestBuilder.() -> Unit = {}
): Result<R> = runCatching { get(urlString, builder).body() }

//use this extenstion function like this
suspend fun performApiCall(): Result<String> {
        return httpClient.getResult<String>("sample_get_url")
    }

class MyViewModel : ViewModel() {
    fun fetchData() {
        viewModelScope.launch {
            val result = repository.performApiCall()
            
            result.onSuccess {
                // Handle success if needed
            }.onFailure {
                // Handle failure if needed
            }
        }
    }
}

默认情况下, runCatching 可能会捕获 CancellationException ,从而影响协程执行。为了防止这种情况,需要编写自己的 runCatching 版本来防止这些问题,KtorBoost 使用自定义的 runCatching 。

public suspend inline fun <R> runSafeSuspendCatching(block: () -> R): Result<R> {
    return try {
        Result.success(block())
    } catch (c: CancellationException) {
        throw c
    } catch (e: Throwable) {
        Result.failure(e)
    }
}

转自:巧用Kotlin Result 类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值