如何规避Kotlin限制"捕获参数禁止使用类型参数" [英] how to circumvent Kotlin's restriction "Type parameter is forbidden for catch parameter"

查看:183
本文介绍了如何规避Kotlin限制"捕获参数禁止使用类型参数"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我定义了以下功能:

inline fun <T> T.tryTo(block: T.() -> Unit): T? {
    try {
        block()
    } catch (ex: IllegalArgumentException) {
        return this
    }
    return null
}

目的是在对象上建立一系列尝试操作,例如:

The purpose is to build a chain of try-actions on an object, e.g.:

val input: String = getInput();

input.tryTo /* treat as a file name and open the file */ {
    Desktop.getDesktop().open(File(this))
}?.tryTo /* treat as a number */ {
    try {
        doSomethingWithTheNumber(parseInt(this))
    } catch (ex: NumberFormatException) {
        throw IllegalArgumentException()
    }
}?.tryTo {
    println("All options tried, none worked out. Don't know how to treat this input.")
}

到目前为止,一切正常.

So far that works fine.

但是,正如您在中间的 tryTo 块(按数字处理")中所看到的那样,将期望的"异常作为IllegalArgumentException抛出以保持架构正常工作是不方便的.这样写会更好:

But, as you can see in the middle tryTo-block ("treat as a number"), it is inconvenient to rethrow an "expected" exception as an IllegalArgumentException to keep the schema working. It would be nicer to write:

val input: String = getInput();

input.tryTo<IllegalArgumentException> /* treat as a file name and open the file */ {
    Desktop.getDesktop().open(File(this))
}?.tryTo<NumberFormatException> /* treat as a number */ {
    doSomethingWithTheNumber(parseInt(this))
}?.tryTo<Exception> {
    println("All options tried, none worked out. Don't know how to treat this input.")
}

因此,我将函数 tryTo 重写为:

So, I have rewritten the function tryTo to:

inline fun <T, X: Exception> T.tryTo(block: T.() -> Unit): T? {
    try {
        block()
    } catch (ex: X) {
        return this
    }
    return null
}

不幸的是,后者无法编译:捕获参数禁止使用类型参数".

Unfortunately, the latter does not compile: "Type parameter is forbidden for catch parameter".

如何规避此限制?

附录:

现在我要去:

inline fun <T, reified X: Exception> T.tryTo(block: T.() -> Unit): T? {
    try {
        block()
    } catch (ex: Exception) {
        return if (ex is X) this else throw ex
    }
    return null
}

但是我对此仍然不满意,因为它要求我明确指定两个类型(类型推断失败..."/预期2个类型参数..."):

But I'm still not happy with this because it requires me to specify both types explicitly ("Type inference failed..." / "2 type arguments expected ..."):

input.tryTo<String, IllegalArgumentException> /* treat as a file in the stapel-directory */ {
    ...
}

虽然从接收者对象可以推断出第一种类型的参数很明显.

though the first type parameter is obvious as inferable from the receiver object.

推荐答案

我认为,只要对类型参数进行类型化,这是可能的,但显然并非如此.我确实找到了此检查的来源,并且很明显,对于catch子句中的任何类型参数,无论其是否经过了整形,都会出错.

I thought this would be possible if you just made the type parameter reified, but apparently it is not. I did find the source of this check, and it quite clearly errors for any sort of type parameter in a catch clause, whether it's reified or not.

添加了这些检查的提交消息引用了此问题-显然是catch子句使用类型参数的对象将捕获所有抛出的Exception实例,如果异常不是指定的类型,则使用ClassCastException崩溃.

The commit message that added these checks references this issue - apparently the catch clause with a type parameter was catching all thrown Exception instances, and crashing with a ClassCastException if the exception wasn't of the specified type.

针对您的情况的一种可能的解决方法来自针对类似Java问题的此答案-如果泛型类型已经过验证,您可以检查引发的异常是否属于该特定类型,我相信这会使您正在寻找该功能:

A possible workaround for your case comes from this answer for the similar Java question - if the generic type is reified, you can check if the exception thrown was of that specific type, which I believe makes this function what you're looking for:

inline fun <T, reified X : Exception> T.tryTo(block: T.() -> Unit): T? {
    try {
        block()
    } catch (ex: Exception) {
        if (ex is X) {
            return this
        }
    }
    return null
}

尽管调用站点变得很丑陋,因为如果函数调用具有两个类型参数,则不能只指定它的第二个类型参数:

Although the call site gets quite ugly because you can't just specify the second type parameter of a function call if it has two type parameters:

val input: String = getInput()

input.tryTo<String, IllegalArgumentException> /* treat as a file name and open the file */ {
    Desktop.getDesktop().open(File(this))
}?.tryTo<String, NumberFormatException> /* treat as a number */ {
    doSomethingWithTheNumber(parseInt(this))
}?.tryTo<String, Exception> {
    println("All options tried, none worked out. Don't know how to treat this input.")
}


上面的方法更好一些,并且更接近原始的Java答案:


A slightly nicer alternative to the above, and closer to the original Java answer:

inline fun <T> T.tryTo(exceptionType: KClass<out Exception>, block: T.() -> Unit): T? {
    try {
        block()
    } catch (ex: Exception) {
        if (exceptionType.isInstance(ex)) {
            return this
        }
    }
    return null
}

像这样传递KClass实例:

input.tryTo(IllegalArgumentException::class) /* treat as a file name and open the file */ {
    Desktop.getDesktop().open(File(this))
}?.tryTo(NumberFormatException::class) /* treat as a number */ {
    doSomethingWithTheNumber(parseInt(this))
}?.tryTo(Exception::class) {
    println("All options tried, none worked out. Don't know how to treat this input.")
}

这篇关于如何规避Kotlin限制&quot;捕获参数禁止使用类型参数&quot;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆