Home > database >  What is Kotlin's functional equivalent for finally?
What is Kotlin's functional equivalent for finally?

Time:11-16

This example is from the documentation of 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();
}

The documentation says:

Once the response body has been read, the HttpURLConnection should be closed by calling disconnect().

I tried use the Java class to load an image in a functional style:

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

Now I am wondering how to add the disconnect call?

I want to achieve this:

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()
    }
}

But in a less ugly manner.

CodePudding user response:

Your existing way of doing this is clear enough as it is. Kotlin is not a purely functional language, so trying to use higher-order functions all the time can sometimes make your code harder to read (See also Principle of least astonishment), not to mention that you are using a Java API, which isn't designed for something like this at all.

One way I've thought of though, is:

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()
    }

I do the entire operation that could through inside a runCatching to catch any exceptions, disconnect from the connection, then throw the exception back out again.

In terms of the order of execution, this should be the same as try...finally like this:

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()
    }
}

CodePudding user response:

There isn't a stdlib solution for your case, but kotlin has the use() extension method defined on (Auto)Closeable to make this pattern more functional for those interfaces.

You could add a use extension method to HttpUrlConnection yourself that calls its disconnect() method, using the same approach as the source of use.

Of course you would still need to write the try finally once, but it is now hidden when using HttpUrlConnection.

On first sight you'd end up with something like this, you might still need some null handling somewhere.

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()
    
  • Related