kotlin 使用 use 关闭资源

1,440 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

有些资源在不再需要时必须使用 close() 显式关闭。

  • 输入/输出流
  • java.sql.Connection
  • java.io.Reader(FileReader, BufferedReader, CSSParser)
  • java.new.socket,java.util.Scanner

这些资源都是实现了继承 AutoCloseable 的 Closeable 接口。

当对资源的引用丢失时,所有这些资源最终都由垃圾收集器处理,但同时维护资源非常缓慢且成本高昂。因此,最好在不再需要时显式调用 close()。

传统上,这些资源是使用try-catch 块处理的。

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

这种代码既复杂又糟糕。 关闭资源时可能会出现异常,因为此类异常没有单独处理。

此外,如果在 try 块或 finally 块中发生错误,则只会传播其中一个。如果两者都可以传播,那就太好了,但是自己实现这一点将是漫长而复杂的。但是,由于它是一种被广泛使用的通用实现,因此它作为一个名为 use 的函数包含在标准库中。

如果使用 use() 更改之前的代码,如下所示。

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

还有一种形式是接收器(当前代码中的读取器)作为 lambda 参数传递,因此可以简写如下

fun countCharactersInFile(path: String): Int { 
     BufferedReader(FileReader(path)).use { 
        return reader.lineSequence().sumBy { it.length }
    } 
}

因为文件经常被写成资源并且文件经常被逐行读取,所以 Kotlin 标准库还提供了 useLines() 可以用来逐行处理文件。

fun countCharactersInFile(path: String): Int { 
     File(path).useLines { lines -> 
         return lines.sumBy { it.length } 
     }
}

这种处理方式将文件内容逐行保存在内存中,因此可以对大文件进行适当的处理,但缺点是文件的行只能写入一次。如果要多次迭代文件中的特定行,则需要多次打开文件。前面的代码可以简单地写成如下。

fun countCharactersInFile(path: String): Int = File(path).useLines { lines -> lines.sumBy { it.length } }