如何规避Kotlin限制“禁止类型参数用于捕获参数”

vx6bjr1n  于 2023-08-06  发布在  Kotlin
关注(0)|答案(3)|浏览(117)

我定义了如下函数:

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(“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
}


不幸的是,后者无法编译:“类型参数禁止用于catch参数”。

如何规避此限制?

附录:
现在我得到了它:

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 */ {
    ...
}


尽管第一类型参数显然是可从接收器对象推断的。

xoefb8l8

xoefb8l81#

我认为这是可能的,如果你只是使类型参数具体化,但显然不是。我确实找到了这个检查的源代码,很明显,它对catch子句中的任何类型的参数都是错误的,无论它是否被具体化。
添加这些检查的提交消息引用了this issue-显然,带有类型参数的catch子句捕获了所有抛出的Exception示例,如果异常不是指定的类型,则会崩溃并返回ClassCastException
对于你的情况,一个可能的解决方法来自this answer,用于类似的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.")
}

gxwragnw

gxwragnw2#

您可以简单地删除receiver参数
对于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.")
}

字符串

nhaq1z21

nhaq1z213#

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

字符串

相关问题