如何规避 Kotlin 的限制 "Type parameter is forbidden for catch parameter"
how to circumvent Kotlin's restriction "Type parameter is forbidden for catch parameter"
我定义了以下函数:
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 块 ("treat as a number") 中看到的那样,将 "expected" 异常重新抛出为 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
}
不幸的是,后者没有编译:"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
}
但我对此仍然不满意,因为它要求我明确指定 两种 类型("Type inference failed..." / "2 type arguments expected ...") :
input.tryTo<String, IllegalArgumentException> /* treat as a file in the stapel-directory */ {
...
}
虽然第一个类型参数很明显,可以从接收者对象中推断出来。
我认为如果您只是将类型参数具体化,这将是可能的,但显然不是。我确实找到了此检查的 source,并且对于 catch 子句中的任何类型参数,无论是否具体化,它都非常明显地出错。
添加这些检查引用的提交消息 this issue - 显然带有类型参数的 catch 子句正在捕获所有抛出的 Exception
实例,如果异常则 ClassCastException
崩溃不是指定类型。
针对类似 Java 问题的 this answer 可能解决您的问题 - 如果通用类型被具体化,您可以检查抛出的异常是否属于该特定类型,我相信使此功能成为您正在寻找的功能:
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.")
}
你可以简单地删除接收器参数
并且最好使用 ?:
然后 ?.
用于 else
语义
inline fun <reified E : Throwable> runIgnoring(block: () -> Unit): Unit? {
return try {
block()
} catch (e: Throwable) {
if (e is E) null else throw e
}
}
val input: String = getInput()
runIgnoring<IllegalArgumentException> /* treat as a file name and open the file */ {
Desktop.getDesktop().open(File(input))
} ?: runIgnoring<NumberFormatException> /* treat as a number */ {
doSomethingWithTheNumber(parseInt(input))
} ?: run {
println("All options tried, none worked out. Don't know how to treat this input.")
}
我定义了以下函数:
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 块 ("treat as a number") 中看到的那样,将 "expected" 异常重新抛出为 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
}
不幸的是,后者没有编译:"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
}
但我对此仍然不满意,因为它要求我明确指定 两种 类型("Type inference failed..." / "2 type arguments expected ...") :
input.tryTo<String, IllegalArgumentException> /* treat as a file in the stapel-directory */ {
...
}
虽然第一个类型参数很明显,可以从接收者对象中推断出来。
我认为如果您只是将类型参数具体化,这将是可能的,但显然不是。我确实找到了此检查的 source,并且对于 catch 子句中的任何类型参数,无论是否具体化,它都非常明显地出错。
添加这些检查引用的提交消息 this issue - 显然带有类型参数的 catch 子句正在捕获所有抛出的 Exception
实例,如果异常则 ClassCastException
崩溃不是指定类型。
针对类似 Java 问题的 this answer 可能解决您的问题 - 如果通用类型被具体化,您可以检查抛出的异常是否属于该特定类型,我相信使此功能成为您正在寻找的功能:
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.")
}
你可以简单地删除接收器参数
并且最好使用 ?:
然后 ?.
用于 else
语义
inline fun <reified E : Throwable> runIgnoring(block: () -> Unit): Unit? {
return try {
block()
} catch (e: Throwable) {
if (e is E) null else throw e
}
}
val input: String = getInput()
runIgnoring<IllegalArgumentException> /* treat as a file name and open the file */ {
Desktop.getDesktop().open(File(input))
} ?: runIgnoring<NumberFormatException> /* treat as a number */ {
doSomethingWithTheNumber(parseInt(input))
} ?: run {
println("All options tried, none worked out. Don't know how to treat this input.")
}