科特林:赶上延伸

时间:2018-06-18 12:51:05

标签: exception exception-handling kotlin

因为Kotlin不像java那样支持多个catch,所以我想创建扩展来部分解决问题。

fun <T: Throwable>  (() -> Unit).catch(vararg exceptions: KClass<T>, catchBlock: (Throwable) -> Unit) {
    try {
        this()
    } catch (e: Throwable) {
        if (e::class in exceptions) catchBlock(e) else throw e
    }
}

可以像这样调用:

{
  throw NotImplementedException.exception()
}.catch(NotImplementedException::class) {
   //handle it
}

但问题是如果要传递几个不同类型的参数,它就不起作用(类型推断失败):

{
   throw IndexOutOfBoundsException()
}.catch(NotImplementedException::class, IndexOutOfBoundsException::class) {

}

那么如何更改扩展的签名以捕获不同类型的几个异常?

1 个答案:

答案 0 :(得分:4)

让我们看看你试图传递给你的函数的两个试图的类型:

val kclass1: KClass<NotImplementedException> = NotImplementedException::class
val kclass2: KClass<IndexOutOfBoundsException> = IndexOutOfBoundsException::class

虽然它们都是KClass实例,但它们的类型参数不同 - NotImplementedExceptionIndexOutOfBoundsException。这意味着没有找到适合这两种类型的函数的通用T类型参数。

仅出于演示和解释的目的,您可以通过将自己的两种类型都投放到KClass<Throwable>(或KClass<Exception>KClass<RuntimeException,自己明白了)来帮助输入推理,它可以找出泛型类型:

{
    throw IndexOutOfBoundsException()
}.catch(NotImplementedException::class as KClass<Throwable>, IndexOutOfBoundsException::class as KClass<Throwable>) {
    println("Caught something: $it")
}

但真正的解决方案是使用out关键字为KClass个实例的类型参数指定use-site variance

fun <T : Throwable> (() -> Unit).catch(vararg exceptions: KClass<out T>, catchBlock: (Throwable) -> Unit) {
    try {
        this()
    } catch (e: Throwable) {
        if (e::class in exceptions) catchBlock(e) else throw e
    }
}

这样编译器会找到T的类型,它既是指定的Throwable的子类型,又是所有参数的KClass类型参数的超类型 - 这将是{{ 1}}在这种情况下,您可以通过在RuntimeException调用上打开意图操作找到答案(Windows上的 Alt + Enter ,macOS上的⌥↩)并选择catch。这将产生以下结果:

Add explicit type arguments