# 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-handit
refers to each individual line. To reduce confusion, avoid writing code with two different nearbyit
s. - [2] Named arguments prevent confusion from too many
it
s.
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.