如何制作吞下异常的函数

问题描述 投票:2回答:3
inline fun <T> rest(request: () -> T): T = try {
    request()
} catch (e: HttpException) {
    val requestId = e.response().raw().request().header(REQUEST_ID_HEADER) 
    if (requestId != null) {
        Dialog(requestId, R.string.oops).show(fragmentManager, null)
    } else {
        throw e
    }
}

它应该执行一些REST请求(在request参数中),如果它失败,并且它包含指定的HTTP头,则显示带有该头的对话框。

但编译器在与Dialog的行中抱怨,它不会返回T,而是返回Unit。但这基本上就是我想要的!我怎样才能做到这一点?

我想到的一个解决方案是将函数的返回类型设置为T?并返回null,但在Kotlin中这样做很脏。

kotlin
3个回答
5
投票

返回null is not dirty per se。有时使用null可能会被滥用,但这是使用null的完全有效的用例。 Kotlin允许您以安全和好的方式使用null,所以不要害怕使用它!

另一种选择是在显示对话框的情况下也抛出异常(是否存在标题)。

为了选择,您必须问自己,如果显示对话框,调用rest()的代码将会执行什么操作。它必须以某种方式处理T的缺失(无效或例外)。这是因为显示对话框不会终止函数的执行。

最后但并非最不重要的是,还有一个选项来处理rest()方法之外的结果。改善塔拉斯的回答:

sealed class Result<out T : Any> {
    class Success<out T : Any>(val value: T) : Result<T>()
    class ErrorWithId(val exception: Exception, val requestId: String) : Result<Nothing>()
    class Error(val exception: Exception) : Result<Nothing>()
}

inline fun <T : Any> rest(request: () -> T): Result<T> = try {
    Result.Success(request())
} catch (e: HttpException) {
    val requestId = e.response().raw().request().header(REQUEST_ID_HEADER)
    if (requestId != null) {
        Result.RecoverableError(e, requestId)
    } else {
        Result.Error(e)
    }
}

private fun thingCallingRest() {
    val result = rest(::testRequest)
    when (result) {
        is Result.Success -> Log.v("__DEBUG", "success: ${result.value}")
        is Result.ErrorWithId -> Dialog(result.requestId, R.string.oops).show(fragmentManager, null)
        is Result.Error -> throw result.exception
    }
}

1
投票

您可以通过通用Result类型解决它:

sealed class Result<out T : Any> {
    class Success<out T : Any>(val value: T) : Result<T>()
    class Error(val exception: Exception, val requestId: String?) : Result<Nothing>()
}

inline fun <T : Any> rest(request: () -> T): Result<T> = try {
    Result.Success(request())
} catch (e: HttpException) {
    val requestId = e.response().raw().request().header(REQUEST_ID_HEADER)
    Result.Error(e, requestId)
}

private fun testRest() {
    val result = rest(::testRequest)
    when (result) {
        is Result.Success -> Log.v("__DEBUG", "success: ${result.value}")
        is Result.Error -> {
            result.requestId?.let {
                Dialog(it, R.string.oops).show(fragmentManager, null)
            } ?: run {
                throw result.exception
            }
        }
    }
}

0
投票

返回null可能一开始觉得难看,但有哪些选择呢?

选项1:抛出异常

如果您需要有关代码失败原因的更多信息,则应抛出异常。

让我们来看看Kotlin的single函数作为一个例子:

listOf<Int>().single() 

会扔

NoSuchElementException:List为空。

listOf<Int>(1, 1).single { it == 1 } 

会扔

IllegalArgumentException:Collection包含多个匹配元素。

这就是我的意思,有进一步的信息。异常类型和给定消息可能为您提供决定如何继续的方法。

选项2:返回null

如果你只是想知道它是否失败或不返回null将是一个很好的方式来发出信号。甚至Kotlin的标准库例如使用singleOrNull()

listOf<Int>().singleOrNull() // returns null

使用elvis运算符提供回退也非常简洁:

listOf<Int>().singleOrNull() ?: 1 // default element
© www.soinside.com 2019 - 2024. All rights reserved.