Kotlin中的Try-with-resources:如何永远告别finally

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

为什么Java程序员在转向Kotlin时会哭泣?

\n\n

想象一下:你用Java编写代码,打开一个文件,读取数据,然后……忘记关闭它。熟悉的痛楚?在Java中,为此设计了try-with-resources结构,它会自动调用close()。但当开发者转向Kotlin时,他们常常陷入困惑:“我钟爱的try (Resource res = ...)在哪里?”

\n\n

剧透:Kotlin中没有try-with-resources作为独立的语言结构。而这是好消息。取而代之的是,该语言提供了一个更优雅、更简洁的解决方案——use函数。让我们来了解它是如何工作的,以及为什么你的代码会变得更干净。

\n\n

问题:资源需要关闭

\n\n

任何外部资源——文件、网络连接、数据库——都需要手动管理。如果你打开一个FileInputStream而不关闭它,系统可能会“泄漏”(内存泄漏),并且文件可能会被其他进程锁定。

\n\n

在Java中,这个问题有两种解决方法:

\n\n
    \n
  • 旧方法:使用finally块显式调用close()。
  • \n
  • 新方法:try-with-resources(从Java 7开始引入)。
  • \n
\n\n

Kotlin更进一步:它使用扩展函数和Lambda表达式,使代码不仅安全,而且美观。

\n\n

Java中的try-with-resources是什么样的?

\n\n

为了比较,我们来看一下经典的Java代码:

\n\n
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {\n    String line = br.readLine();\n    System.out.println(line);\n} catch (IOException e) {\n    e.printStackTrace();\n}\n// br 自动关闭\n
\n\n

一切正常,但有一些细节:

\n
    \n
  • 需要在try括号内声明变量。
  • \n
  • 仅支持实现了AutoCloseable的类。
  • \n
  • 如果有多个资源,代码会变得冗长。
  • \n
\n\n

Kotlin的方法:use函数

\n\n

在Kotlin中,安全地使用资源是通过use函数实现的。它被定义为Closeable接口(以及为了与Java兼容的AutoCloseable)的扩展函数。

\n\n

以下是在Kotlin中相同的示例:

\n\n
import java.io.BufferedReader\nimport java.io.FileReader\n\nfun main() {\n    BufferedReader(FileReader("file.txt")).use { reader ->\n        val line = reader.readLine()\n        println(line)\n    }\n    // reader 自动关闭\n}\n
\n\n

干净、简洁,没有多余的try-catch?并不完全是。use本身不捕获异常。它只保证在块结束时调用close()。如果需要处理错误,请在外部添加try-catch:

\n\n
try {\n    BufferedReader(FileReader("file.txt")).use { reader ->\n        val line = reader.readLine()\n        println(line)\n    }\n} catch (e: IOException) {\n    println("读取文件时出错:${e.message}")\n}\n
\n\n

use在底层是如何工作的?

\n\n

为了理解其中的魔法,我们来看看Kotlin的源代码。use函数在标准库中的定义大致如下:

\n\n
public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {\n    var exception: Throwable? = null\n    try {\n        return block(this)\n    } catch (e: Throwable) {\n        exception = e\n        throw e\n    } finally {\n        when {\n            exception == null -> close()\n            else -> try {\n                close()\n            } catch (closeException: Throwable) {\n                // 抑制异常,将其添加到主异常中\n                exception.addSuppressed(closeExcep                

博客

书籍推荐