我定义了以下功能:
index=True
目的是在对象上构建一系列try-actions,例如:
inline fun <T> T.tryTo(block: T.() -> Unit): T? {
try {
block()
} catch (ex: IllegalArgumentException) {
return this
}
return null
}
到目前为止一切正常。
但是,正如您在中间看到的那样 tryTo -block(“视为数字”),将“预期”异常重新抛出为IllegalArgumentException以保持模式正常运行是不方便的。写一下会更好:
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 重写为:
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.")
}
不幸的是,后者没有编译:“禁止使用类型参数来捕获参数”。
如何规避此限制?
附录:
现在我已经明白了:
inline fun <T, X: Exception> T.tryTo(block: T.() -> Unit): T? {
try {
block()
} catch (ex: X) {
return this
}
return null
}
但是我仍然对此不满意,因为它要求我明确指定两种类型(“类型推断失败...”/“预期2种类型参数......”):< / p>
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
}
虽然第一个类型参数显然可以从接收器对象中推断出来。
答案 0 :(得分:3)
我认为如果你只是将类型参数设置为reified,这是可能的,但显然它不是。我确实找到了这个检查的source,并且很明显错误的是catch子句中的任何类型的参数,无论它是否已经具体化。
添加了这些检查的提交消息引用了this issue - 显然,带有类型参数的catch子句正在捕获所有抛出的Exception
个实例,如果异常是'n',则崩溃为ClassCastException
t指定类型。
对于类似的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.")
}
答案 1 :(得分:0)
您只需删除接收器参数
对于?:
的语义,最好使用?.
,然后使用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.")
}