【问题标题】:Should I handle exception with Closable.use{...} in Kotlin?我应该在 Kotlin 中使用 Closable.use{...} 处理异常吗?
【发布时间】:2020-09-21 03:55:59
【问题描述】:

根据Closable.use的来源,如果发生错误,会抛出异常。

public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
    var exception: Throwable? = null
    try {
        return block(this)
    } catch (e: Throwable) {
        exception = e
        throw e
    } finally {
        when {
            apiVersionIsAtLeast(1, 1, 0) -> this.closeFinally(exception)
            this == null -> {}
            exception == null -> close()
            else ->
                try {
                    close()
                } catch (closeException: Throwable) {
                    // cause.addSuppressed(closeException) // ignored here
                }
        }
    }

Closable.use 的大多数示例中,不使用try-catch,如下所示。 为什么不需要错误处理?安全吗?

    BufferedReader(FileReader("test.file")).use { return it.readLine() }

【问题讨论】:

  • 本问答适用于 Java,但同样的概念也适用于 Kotlin:When to catch the Exception vs When to throw the Exceptions?
  • @Tenfour04 try/catch 不应该包含使用函数吗?
  • @Tenfour04 @Tenfour04 use 可以抛出我认为 OP 担心的异常。
  • 将 try/catch 放入 use 就像做传统的 try/catch/finally 一样。将其放在外面会导致在对象关闭之后处理异常。如果关闭对象的行为本身会引发异常,那么您需要在use 之外捕获它。这种行为类似于 Java 的 try-with-resources。
  • 有趣的是,use 在处理来自虚拟 tryfinally 块的堆叠异常时与 Java 的 try-with-resources 不同。 use 抑制来自close 的异常,但try-with-resources 抑制来自try 的异常。

标签: kotlin


【解决方案1】:

我们从 Kotlin 文档中看到 use 函数的目的是什么:

在这个资源上执行给定的块函数然后关闭它 无论是否抛出异常,都正确向下。

如果块函数成功完成或抛出异常,此函数会正确关闭资源。处理块函数的结果是您的责任。

如果抛出异常并且有办法处理它并继续执行代码,请使用 try/catch。如果没有什么可做的并且应该将控制权传递给调用者,则没有必要使用 try/catch。

【讨论】:

    【解决方案2】:

    这一行

     BufferedReader(FileReader("test.file")).use { return it.readLine() }
    

    不安全。读取和关闭 reader 都可以抛出 IOExceptions,它们不是 RuntimeExceptions(由编程错误引起)。这意味着让它们不被捕获会使您的应用因您无法控制的事情而崩溃。

    由于 Kotlin 没有检查异常,编译器不会就此发出警告。为了安全地执行此操作,您需要将其包装在 try/catch 中。如果您想以不同于处理关闭错误的方式处理读取错误,则需要有内部和外部的 try/catch 语句:

    try { 
        BufferedReader(FileReader("test.file")).use { 
            try {
                return it.readLine()
            catch (e: IOException) {
                println("Failed to read line")
            }
        }
    } catch (e: IOException) {
        println("Failed to close reader")
    }
    

    或者包装整个东西并提取任何被抑制的异常,但是区分它们很麻烦:

    try {
        BufferedReader(FileReader("test.file")).use { return it.readLine() }
    } catch (e: IOException) {
        val throwables = listOf(e, *e.suppressed)
        for (throwable in throwables)
            println(throwable.message)
    }
    

    但实际上,您可能不会对各种 IOExceptions 做出不同的反应,因此您可以将一个 try/catch 放在外面。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-16
      • 1970-01-01
      • 2021-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-24
      • 2021-04-01
      相关资源
      最近更新 更多