kotlinfunctional-programmingfinally

What is Kotlin's functional equivalent for finally?


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.


Solution

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