什么是Kotlin的函数等价的finally?

xdnvmnnf  于 2022-11-16  发布在  Kotlin
关注(0)|答案(2)|浏览(156)

此示例摘自HttpUrlConnection的文档:

URL url = new URL("http://www.android.com/");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
    InputStream in = new BufferedInputStream(urlConnection.getInputStream());
    readStream(in);
}
finally {
    urlConnection.disconnect();
}

该文档说明:
读取响应正文后,应通过调用disconnect()关闭HttpURLConnection。
我尝试使用Java类以函数样式加载图像:

fun fetch (url: String): ImageBitmap =
    URL(url)
        .openConnection()
        .also { it.setRequestProperty (authorization.header, authorization.basicauth()) }
        .getInputStream()
        .buffered()
        .use { BitmapFactory.decodeStream(it) }
        .asImageBitmap()

现在我想知道如何添加disconnect调用?
我要实现这一点:

fun fetch (url: String): ImageBitmap {
    var connection: HttpURLConnection? = null
    return try {
        URL(url)
            .openConnection()
            .also { it.setRequestProperty(authorization.header, authorization.basicauth()) }
            .also { connection = it as HttpURLConnection }
            .getInputStream()
            .buffered()
            .use { BitmapFactory.decodeStream(it) }
            .asImageBitmap()
    } finally {
        connection?.disconnect()
    }
}

但不那么难看。

eit6fx6z

eit6fx6z1#

Kotlin不是一种纯粹的函数式语言,因此 * 一直.尝试使用高阶函数有时会使代码 * 更难 * 阅读(另请参阅Principle of least astonishment),更不用说您使用的 Java API,它根本不是为这种情况设计的。
我想到的一个办法是:

fun fetch (url: String) =
    (URL(url).openConnection() as HttpURLConnection).apply {
        runCatching {
            setRequestProperty(authorization.header, authorization.basicauth())
            inputStream
                .buffered()
                .use { BitmapFactory.decodeStream(it) }
                .asImageBitmap()
        }.also { disconnect() }.getOrThrow()
    }

我在runCatching中执行整个操作,以捕获任何异常,断开连接,然后再次抛出异常。
就执行顺序而言,这应该与try...finally相同,如下所示:

fun fetch (url: String): ImageBitmap {
    val connection = URL(url).openConnection() as HttpURLConnection
    return try {
        connection
            .also { it.setRequestProperty(authorization.header, authorization.basicauth()) }
            .getInputStream()
            .buffered()
            .use { BitmapFactory.decodeStream(it) }
            .asImageBitmap()
    } finally {
        connection.disconnect()
    }
}
2jcobegt

2jcobegt2#

对于您的情况,没有stdlib解决方案,但是Kotlin在(Auto)Closeable上定义了use()扩展方法,以使该模式对那些接口更有效。
您可以自己向HttpUrlConnection添加一个use扩展方法,该方法调用其disconnect()方法,使用与源代码相同的方法。
当然,您仍然需要编写一次try finally,但现在使用HttpUrlConnection时它是隐藏的。
第一眼看到的结果是这样的,但在某些地方可能还需要一些null处理。

public fun <T : HttpURLConnection, R> T.use(block: (T) -> R): R {
    try {
        return block(this)
    } finally {
        disconnect()
    }
}

(URL(url).openConnection() as HttpURLConnection).use {
   // do things with connection `it`
}
// connection is now disconnected()

相关问题