Effective Kotlin - 关闭正在使用的资源

使用 use api 关闭已完成使用的资源

在我们使用的资源中,有些资源在使用完成后不会自动停止状态。我们需要通过 close 方法等将这些资源的状态更改为未使用状态。

Kotlin/JVM 中使用的 Java 标准库包含了许多此类资源。以下是典型的不会自动释放的资源:


- InputStream and OutputStream
- Java.sql.Connection
- Java.io.Reader ( FileReader, BufferedReader, CSSParser )
- Java.new.Socket and java.util.Scanner

所有这些资源都支持继承自 AutoCloseableCloseable 接口。

请查看以下代码片段:


fun countCharactersInFile(path:String):Int{
    val reader = BufferedReader(FileReader(path))
    try {
        return reader.lineSequence().sumBy { it.length }
    }
    finally {
       reader.close()
    }
}

上面的代码既复杂又不严谨。因为如果 finally 块中的 reader.close() 抛出异常,则无法处理。如果要处理这种情况,代码可能会变成这样:


fun countCharactersInFile(path:String):Int{
    val reader = BufferedReader(FileReader(path))
    try {
        return reader.lineSequence().sumBy { it.length }
    }
    finally {
       try { reader.close() } catch (e: Exception) {}
    }
}

这种实现方式既冗长又复杂,但由于它是通用做法,因此被提取到了标准库函数 use 中。该功能从 Kotlin 1.2 版本开始支持。

以下是参考书中的示例以及补充示例。


try {
    Socket("open", 80).use { socket ->
        socket.getInputStream().use { inputStream ->
            InputStreamReader(inputStream).use { reader ->
                println(reader.readLines())
            }
        }
    }
} catch (e: Exception) {
        // ...
}

使用 use 时,如果存在嵌套情况,使用默认的 it 可能会产生问题,因此建议根据作用域明确声明变量名后再使用。

try-with-resources

我们习惯使用的 try-catch 处理资源的方式有时会使代码变得复杂且混乱。为此,Java 提供了 try-with-resources 功能。简而言之,它会在 try 块结束时自动关闭在该 try 中使用的资源。


public static String getHtml(String url) throws IOException {

    val targetUrl = URL(url);

    try (val inputSR = new InputStreamReader(targetUrl.openStream());val bufferReader =  BufferedReader(inputSR)){
        val html = StringBuffer();
        var tmp;

        while ((tmp = reader.readLine()) != null) {
            html.append(tmp);
        }
        return html.toString();
    }
}

推荐阅读的文章

AD