Min*_*eet 6 generics exception-handling kotlin
我定义了以下函数:
inline fun <T> T.tryTo(block: T.() -> Unit): T? {
    try {
        block()
    } catch (ex: IllegalArgumentException) {
        return this
    }
    return null
}
目的是在对象上构建一系列尝试操作,例如:
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.")
}
到目前为止,效果很好。
但是,正如您在中间tryTo -block(“视为数字”)中所见,将“预期”异常重新抛出为 IllegalArgumentException 以保持架构正常工作是不方便的。最好写成:
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重写为:
inline fun <T, X: Exception> T.tryTo(block: T.() -> Unit): T? {
    try {
        block()
    } catch (ex: X) {
        return this
    }
    return null
}
不幸的是,后者不能编译:“类型参数被禁止用于捕获参数”。
如何规避这个限制?
附录:
现在我已经做到了:
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 个类型参数...”):
input.tryTo<String, IllegalArgumentException> /* treat as a file in the stapel-directory */ {
    ...
}
尽管第一个类型参数很明显可以从接收器对象推断出来。
zsm*_*b13 10
我认为如果您只是使类型参数具体化,这是可能的,但显然不是。我确实找到了这个检查的来源,并且很明显,catch 子句中任何类型的类型参数都会出错,无论它是否具体化。
添加这些检查的提交消息引用了这个问题- 显然,带有类型参数的 catch 子句正在捕获所有抛出的Exception实例,ClassCastException如果异常不是指定类型的,则崩溃。
您的情况的可能解决方法来自类似 Java 问题的这个答案- 如果泛型类型被具体化,您可以检查抛出的异常是否属于该特定类型,我相信这使该函数成为您正在寻找的:
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
}
尽管调用站点变得非常难看,因为如果函数调用有两个类型参数,则不能只指定它的第二个类型参数:
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 答案:
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.")
}