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

所有對應的資源都支援繼承自 AutoCloseable Closeable 介面。

請檢查以下語法。


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 可能會產生問題,因此建議根據作用域(Scope)進行宣告使用。

try with resources

我們習慣用 try-catch 來處理資源,但程式碼有時會變得複雜且雜亂。為了解決這個問題,提供了 try-with-resources 功能。簡單來說,它會在 try 區塊結束時,自動對在 try 中使用的資源執行 close 處理。


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