Kotlin中的Try-with-resources

197

当我尝试在Kotlin中编写Java try-with-resources语句的等效代码时,没有成功。

我尝试了以下不同的变体:

try (writer = OutputStreamWriter(r.getOutputStream())) {
    // ...
}

但是两者都不起作用。有人知道应该使用什么吗?

显然 Kotlin 语法 不包括 这样的结构,但也许我漏掉了什么。它将 try 块的语法定义如下:

try : "try" block catchBlock* finallyBlock?;
5个回答

283

kotlin-stdlib中有一个use函数(源代码)。

使用方法:

OutputStreamWriter(r.getOutputStream()).use {
    // `it` is your OutputStreamWriter
    it.write('a')
}

5
我非常喜欢扩展方法。有很多事情可以做,而且不需要额外的语言特性。 - Kirill Rakhman
25
此外,实际上还有一个扩展属性可以获取OutputStreamWriterr.outputStream.writer.use { ... } - Damian Wieczorek
4
展示 use 扩展的参考文档链接为:http://kotlinlang.org/docs/reference/idioms.html#java-7s-try-with-resources。 - Javaru
5
我该如何更好地使用多个"use"?FileOutputStream(into).use { val mergingStream = BufferedOutputStream(it).use { } }.use 可以用于确保代码块执行完毕后自动关闭资源。在这个例子中,它确保文件输出流(FileOutputStream)和缓冲输出流(BufferedOutputStream)在完成后被正确地关闭。这种方式可以简化代码并提高可读性。 - Ponomarenko Oleh
关于刷新怎么办?我应该在使用块结束时刷新OutputStream吗? - Hadi
显示剩余2条评论

76

TL;DR: 没有特殊的语法,只有一个函数

Kotlin与Java不同,没有专门的语法。相反,try-with-resources被作为标准库函数use提供。

FileInputStream("filename").use { fis -> //or implicit `it`
   //use stream here
} 

使用实现

@InlineOnly
public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
    var closed = false
    try {
        return block(this)
    } catch (e: Exception) {
        closed = true
        try {
            this?.close()
        } catch (closeException: Exception) {
        }
        throw e
    } finally {
        if (!closed) {
            this?.close()
        }
    }
}

这个函数被定义为所有Closeable?类型的通用扩展。 Closeable是Java的interface,自Java SE7以来允许使用try-with-resources
该函数接受一个函数字面量block,在try中执行。与Java中的try-with-resources相同,Closeablefinally中被关闭

同时,在block内发生的故障会导致close执行,在可能的异常情况下,这些异常被简单地忽略。 try-with-resources不同,因为这种异常可以在Java的解决方案中请求。

如何使用

use扩展可用于任何Closeable类型,即流、读取器等。

FileInputStream("filename").use {
   //use your stream by referring to `it` or explicitly give a name.
} 

花括号中的部分将成为use中的block(这里传递了一个lambda作为参数)。在块执行完毕后,您可以确保FileInputStream已关闭。


16

编辑:以下回答仍适用于Kotlin 1.0.x。对于Kotlin 1.1,有支持针对Java 8的标准库以支持可关闭资源模式。

对于不支持"use"函数的其他类,我已经自制了以下try-with-resources:

package info.macias.kotlin

inline fun <T:AutoCloseable,R> trywr(closeable: T, block: (T) -> R): R {
    try {
        return block(closeable);
    } finally {
        closeable.close()
    }
}

然后,您可以按照以下方式使用它:
fun countEvents(sc: EventSearchCriteria?): Long {
    return trywr(connection.prepareStatement("SELECT COUNT(*) FROM event")) {
        var rs = it.executeQuery()
        rs.next()
        rs.getLong(1)
    }
}

5
这段代码不能正确处理来自finally子句抛出的异常,这也是Java中添加try-with-resources语句的原因之一。这只是一个简单的try/finally块。 - Nikola Mihajlović

2

我强烈建议在类中使用AutoCloseable。

当对象在资源规范头中声明时,AutoCloseable对象会在退出try-with-resources块时自动调用。

例子:

class Resource : AutoCloseable {
    fun op1() = println("op1")
    override fun close() = println("close up.")
}

在主函数中:

Resource().use {
    it.op1()
}

输出:

> op1
close up.

1

由于这篇StackOverflow帖子在当前搜索结果中接近顶部,搜索关键词为“kotlin closeable example”,但其他答案(包括官方文档)都没有清楚地解释如何扩展Closeable(又称java.io.Closeable),因此我想添加一个例子来说明如何创建扩展Closeable的自定义类。步骤如下:

import java.io.Closeable

class MyServer : Closeable {
    override fun close() {
        println("hello world")
    }
}

然后使用它:

fun main() {
    val s = MyServer()
    s.use {
        println("begin")
    }
    println("end")
}

在 Kotlin Playground 这里 查看此示例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接