Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

资源清理

使用 try-finally 块进行资源清理是繁琐且容易出错的。Kotlin 的库函数可以为您管理清理。

就像您在 异常处理 中学到的一样,finally 子句会在 try 块退出时无论如何清理资源。但是如果在关闭资源时可能发生异常怎么办?你最终会在 finally 子句内部添加另一个 try 块。而且,如果在 try 内部抛出一个异常,并在关闭资源时抛出另一个异常,后者不应该遮盖前者。确保适当的清理变得非常混乱。

为了减少这种复杂性,Kotlin 的 use() 保证关闭资源的适当清理,使您不必编写手动的清理代码。

use() 可以与实现了 Java 的 AutoCloseable 接口的任何对象一起使用。它执行块内的代码,然后在对象上调用 close(),无论您如何退出块,无论是正常退出(包括通过 return),还是通过异常退出。

use() 会重新抛出所有的异常,因此您仍然必须处理这些异常。

use() 一起使用的预定义类在 Java 的 AutoCloseable 文档中可以找到。例如,要从 File 中读取行,我们可以将 use() 应用于 BufferedReaderCheck Instructions(se06-ch02.md)中的 DataFile 继承了 java.io.File

// ResourceCleanup/AutoCloseable.kt
import atomictest.eq
import checkinstructions.DataFile

fun main() {
  DataFile("Results.txt")
    .bufferedReader()
    .use { it.readLines().first() } eq
    "Results"
}

useLines() 打开一个 File 对象,提取所有的行,并将这些行传递给目标函数(通常是一个 lambda):

// ResourceCleanup/UseLines.kt
import atomictest.eq
import checkinstructions.DataFile

fun main() {
  DataFile("Results.txt").useLines {
    it.filter { "#" in it }.first()    // [1]
  } eq "# ok"
  DataFile("Results.txt").useLines { lines ->
    lines.filter { line ->             // [2]
      "#" in line
    }.first()
  } eq "# ok"
}
  • [1] 左侧的 it 是文件中所有行的集合,而右侧的 it 是每一行。为了减少混淆,避免编写同时出现两个不同的 it 的代码。
  • [2] 使用命名参数可以避免 it 过多引起混淆。

所有操作都在 useLines() lambda 内部完成;在 lambda 之外,文件内容不可用,除非您显式返回它们。当关闭文件时,useLines() 会返回 lambda 的结果。

forEachLine() 使得可以轻松地对文件中的每一行应用一个操作:

// ResourceCleanup/ForEachLine.kt
import checkinstructions.DataFile
import atomictest.*

fun main() {
  DataFile("Results.txt").forEachLine {
    if (it.startsWith("#"))
      trace("$it")
  }
  trace eq "# ok"
}

forEachLine() 中的 lambda 返回 Unit,这意味着您对行所做的任何操作必须通过副作用实现。在函数式编程中,我们更喜欢返回结果而不是副作用,因此 useLines()forEachLine() 更符合函数式编程的方式。但是,forEachLine() 对于简单的实用程序来说是一种快速的解决方案。

您可以通过实现 AutoCloseable 接口来创建自己的类,以便与 use() 一起使用,该接口仅包含 close() 函数:

// ResourceCleanup/Usable.kt
package resourcecleanup
import atomictest.*

class Usable() : AutoCloseable {
  fun func() = trace("func()")
  override fun close() = trace("close()")
}

fun main() {
  Usable().use { it.func() }
  trace eq "func() close()"
}

use() 确保资源在创建资源的位置进行清理,而不是在您完成资源时强制您编写清理代码。

Exercises and solutions can be found at www.AtomicKotlin.com.