Kotlin:catch 扩展
Kotlin: catch extension
因为 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) {
}
那么如何更改扩展的签名以捕获不同类型的多个异常?
让我们看看您要传递给函数的两个参数的类型:
val kclass1: KClass<NotImplementedException> = NotImplementedException::class
val kclass2: KClass<IndexOutOfBoundsException> = IndexOutOfBoundsException::class
虽然它们都是 KClass 实例,但它们的类型参数不同 - NotImplementedException
和 IndexOutOfBoundsException
。这意味着找不到适合这两种类型的函数的通用 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
类型参数的超类型 - 这将是RuntimeException
在这种情况下,您可以通过在 catch
调用(Alt + Enter on Windows、⌥↩ 在 macOS 上)并选择 Add explicit type arguments
。这将产生以下内容:
{
throw IndexOutOfBoundsException()
}.catch<RuntimeException>(NotImplementedException::class, IndexOutOfBoundsException::class) {
println("Caught something: $it")
}
因为 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) {
}
那么如何更改扩展的签名以捕获不同类型的多个异常?
让我们看看您要传递给函数的两个参数的类型:
val kclass1: KClass<NotImplementedException> = NotImplementedException::class
val kclass2: KClass<IndexOutOfBoundsException> = IndexOutOfBoundsException::class
虽然它们都是 KClass 实例,但它们的类型参数不同 - NotImplementedException
和 IndexOutOfBoundsException
。这意味着找不到适合这两种类型的函数的通用 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
类型参数的超类型 - 这将是RuntimeException
在这种情况下,您可以通过在 catch
调用(Alt + Enter on Windows、⌥↩ 在 macOS 上)并选择 Add explicit type arguments
。这将产生以下内容:
{
throw IndexOutOfBoundsException()
}.catch<RuntimeException>(NotImplementedException::class, IndexOutOfBoundsException::class) {
println("Caught something: $it")
}