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

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

我定义了如下函数:

  1. inline fun <T> T.tryTo(block: T.() -> Unit): T? {
  2. try {
  3. block()
  4. } catch (ex: IllegalArgumentException) {
  5. return this
  6. }
  7. return null
  8. }

字符串
其目的是在对象上构建尝试操作链,例如:

  1. val input: String = getInput();
  2. input.tryTo /* treat as a file name and open the file */ {
  3. Desktop.getDesktop().open(File(this))
  4. }?.tryTo /* treat as a number */ {
  5. try {
  6. doSomethingWithTheNumber(parseInt(this))
  7. } catch (ex: NumberFormatException) {
  8. throw IllegalArgumentException()
  9. }
  10. }?.tryTo {
  11. println("All options tried, none worked out. Don't know how to treat this input.")
  12. }


到目前为止一切正常。
但是,正如您在中间的 tryTo-block(“treat as a number”)中所看到的,将“expected”异常作为IllegalArgumentException重新抛出以保持模式工作是不方便的。最好是这样写:

  1. val input: String = getInput();
  2. input.tryTo<IllegalArgumentException> /* treat as a file name and open the file */ {
  3. Desktop.getDesktop().open(File(this))
  4. }?.tryTo<NumberFormatException> /* treat as a number */ {
  5. doSomethingWithTheNumber(parseInt(this))
  6. }?.tryTo<Exception> {
  7. println("All options tried, none worked out. Don't know how to treat this input.")
  8. }


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

  1. inline fun <T, X: Exception> T.tryTo(block: T.() -> Unit): T? {
  2. try {
  3. block()
  4. } catch (ex: X) {
  5. return this
  6. }
  7. return null
  8. }


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

如何规避此限制?

附录:
现在我得到了它:

  1. inline fun <T, reified X: Exception> T.tryTo(block: T.() -> Unit): T? {
  2. try {
  3. block()
  4. } catch (ex: Exception) {
  5. return if (ex is X) this else throw ex
  6. }
  7. return null
  8. }


但我仍然不满意,因为它要求我显式地指定两个类型(“类型推断失败...”/“需要2个类型参数...”):

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


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

xoefb8l8

xoefb8l81#

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

  1. inline fun <T, reified X : Exception> T.tryTo(block: T.() -> Unit): T? {
  2. try {
  3. block()
  4. } catch (ex: Exception) {
  5. if (ex is X) {
  6. return this
  7. }
  8. }
  9. return null
  10. }

字符串
虽然调用位置变得非常丑陋,因为如果函数调用有两个类型参数,你不能只指定它的第二个类型参数:

  1. val input: String = getInput()
  2. input.tryTo<String, IllegalArgumentException> /* treat as a file name and open the file */ {
  3. Desktop.getDesktop().open(File(this))
  4. }?.tryTo<String, NumberFormatException> /* treat as a number */ {
  5. doSomethingWithTheNumber(parseInt(this))
  6. }?.tryTo<String, Exception> {
  7. println("All options tried, none worked out. Don't know how to treat this input.")
  8. }


一个稍微好一点的替代方案,更接近原始的Java答案:

  1. inline fun <T> T.tryTo(exceptionType: KClass<out Exception>, block: T.() -> Unit): T? {
  2. try {
  3. block()
  4. } catch (ex: Exception) {
  5. if (exceptionType.isInstance(ex)) {
  6. return this
  7. }
  8. }
  9. return null
  10. }


传入KClass示例如下:

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

展开查看全部
gxwragnw

gxwragnw2#

您可以简单地删除receiver参数
对于else语义,最好使用?:,然后使用?.

  1. inline fun <reified E : Throwable> runIgnoring(block: () -> Unit): Unit? {
  2. return try {
  3. block()
  4. } catch (e: Throwable) {
  5. if (e is E) null else throw e
  6. }
  7. }
  8. val input: String = getInput()
  9. runIgnoring<IllegalArgumentException> /* treat as a file name and open the file */ {
  10. Desktop.getDesktop().open(File(input))
  11. } ?: runIgnoring<NumberFormatException> /* treat as a number */ {
  12. doSomethingWithTheNumber(parseInt(input))
  13. } ?: run {
  14. println("All options tried, none worked out. Don't know how to treat this input.")
  15. }

字符串

展开查看全部
nhaq1z21

nhaq1z213#

  1. inline fun <T, reified X: Exception> T.tryTo(block: T.() -> Unit): T? {
  2. return try {
  3. block()
  4. } catch (ex: Exception) {
  5. when(ex) {
  6. is E -> this
  7. else null
  8. }
  9. }
  10. }

字符串

相关问题