# Resource Cleanup

Using try-finally blocks for resource cleanup is tedious and error-prone. Kotlin’s library functions manage cleanup for you.

As you learned in [Exception Handling](javascript:void(0)), the finally clause cleans up resources regardless of how the try block exits. But what if an exception can happen while closing a resource? You end up with another try inside the finally clause. On top of that, if one exception is thrown inside a try and another while closing the resource, the latter shouldn’t conceal the former. Ensuring proper cleanup becomes very messy.

To reduce this complexity, Kotlin’s use() guarantees proper cleanup of closeable resources, liberating you from handwritten cleanup code.

use() works with any object that implements Java’s AutoCloseable interface. It executes the code within the block, then calls close() on the object, regardless of how you exit the block—either normally (including via return), or through an exception.

use() rethrows all exceptions, so you must still deal with those exceptions.

Predefined classes that work with use() are found in the Java documentation for AutoCloseable. For example, to read lines from a File we apply use() to a BufferedReader. DataFile from [Check Instructions](javascript:void(0)) inherits java.io.File:

// ResourceCleanup/AutoCloseable.kt
import atomictest.eq
import checkinstructions.DataFile

fun main() {
  DataFile("Results.txt")
    .bufferedReader()
    .use { it.readLines().first() } eq
    "Results"
}

useLines() opens a File object, extracts all its lines, and passes those lines to a target function (typically a lambda):

// ResourceCleanup/UseLines.kt
import atomictest.eq
import checkinstructions.DataFile

fun main() {
  DataFile("Results.txt").useLines {
    it.filter { "#" in it }.first()    // [1]
  } eq "# ok"
  DataFile("Results.txt").useLines { lines ->
    lines.filter { line ->             // [2]
      "#" in line
    }.first()
  } eq "# ok"
}
  • [1] The left-hand it refers to the collection of lines in the file, while the right-hand it refers to each individual line. To reduce confusion, avoid writing code with two different nearby its.
  • [2] Named arguments prevent confusion from too many its.

Everything happens within the useLines() lambda; outside the lambda the file contents are unavailable unless you explicitly return them. As it closes the file, useLines() returns the result of the lambda.

forEachLine() makes it easy to apply an action to each line in a file:

// ResourceCleanup/ForEachLine.kt
import checkinstructions.DataFile
import atomictest.*

fun main() {
  DataFile("Results.txt").forEachLine {
    if (it.startsWith("#"))
      trace("$it")
  }
  trace eq "# ok"
}

The lambda in forEachLine() returns Unit, which means that anything you do with the lines must be achieved through side effects. In functional programming, we prefer returning results over side effects, and thus useLines() is a more functional approach than forEachLine(). However, forEachLine() is a quick solution for simple utilities.

You can create your own class that works with use() by implementing the AutoCloseable interface, which contains only the close() function:

// ResourceCleanup/Usable.kt
package resourcecleanup
import atomictest.*

class Usable() : AutoCloseable {
  fun func() = trace("func()")
  override fun close() = trace("close()")
}

fun main() {
  Usable().use { it.func() }
  trace eq "func() close()"
}

use() ensures resource cleanup at the point the resource is created, rather than forcing you to write cleanup code when you’re finished with the resource.

Exercises and solutions can be found at www.AtomicKotlin.com.