Question

How to Handle Try-with-Resources in Kotlin

kotlintry-with-resources

Question

In Java, I would normally use a try-with-resources statement to automatically close a resource such as a writer or stream. When I tried to write an equivalent in Kotlin, it did not work.

For example, I tried variations like this:

try (writer = OutputStreamWriter(r.getOutputStream())) {
    // ...
}

However, Kotlin does not accept this syntax. Is there a Kotlin equivalent to Java's try-with-resources?

I also noticed that Kotlin's grammar for a try block is defined like this:

try : "try" block catchBlock* finallyBlock?;

So it seems Kotlin does not include Java-style try-with-resources syntax directly. What should be used instead?

Short Answer

By the end of this page, you will understand why Kotlin does not support Java-style try (...) {} syntax, and how to achieve the same result using Kotlin's use() function. You will also learn when resources are closed, how exceptions behave, and how this pattern is used in real Kotlin code.

Concept

Kotlin does not have a special language construct that matches Java's try-with-resources syntax.

In Java, this is valid:

try (BufferedReader reader = new BufferedReader(...)) {
    // use reader
}

That syntax is part of the Java language itself.

In Kotlin, the equivalent idea is usually handled with the use() extension function. Instead of adding resource management to the try syntax, Kotlin provides a standard library function that:

  • works with a closable resource
  • runs a block of code with that resource
  • automatically closes the resource when the block finishes
  • still closes the resource if an exception happens

Typical Kotlin style looks like this:

OutputStreamWriter(r.getOutputStream()).use { writer ->
    writer.write("Hello")
}

This matters because resource management is a common programming task. Files, sockets, database streams, readers, and writers all need to be closed reliably. Forgetting to close them can cause:

  • memory pressure
  • file descriptor leaks
  • locked files
  • network connection issues
  • hard-to-debug production errors

So even though Kotlin does not copy Java's syntax, it still gives you a safe and idiomatic way to handle resources.

Mental Model

Think of a resource like borrowing a tool from a workshop.

  • You borrow the tool: open a file, create a stream, get a writer.
  • You use it for a task.
  • You must return it when finished.

Java says: "Borrow the tool inside the try (...) header, and Java will return it for you."

Kotlin says: "Take the tool, call use { ... }, do your work inside the block, and Kotlin will return it for you afterward."

So use() is like a supervised borrowing process:

  1. open resource
  2. do work inside the block
  3. close resource automatically
  4. even if something goes wrong, still close it
Take Quiz

Syntax and Examples

The basic Kotlin syntax is:

resource.use { value ->
    // work with value
}

For a writer, that becomes:

val output = r.getOutputStream()
OutputStreamWriter(output).use { writer ->
    writer.write("Hello, Kotlin")
}

You can also chain resource creation directly:

OutputStreamWriter(r.getOutputStream()).use { writer ->
    writer.write("Hello")
    writer.flush()
}

Reading from a file

java.io.File("data.txt").bufferedReader().use { reader ->
    val text = reader.readLine()
    println(text)
}

Writing to a file

java.io.File("log.txt").bufferedWriter().use { writer ->
    writer.write("Application started")
}

Why this works

use() executes the lambda block and closes the resource afterward. This gives Kotlin the same practical benefit as Java's try-with-resources, but with a library function instead of special syntax.

Step by Step Execution

Consider this example:

java.io.File("notes.txt").bufferedWriter().use { writer ->
    writer.write("Line 1")
}
println("Done")

Here is what happens step by step:

  1. java.io.File("notes.txt") creates a File object.
  2. .bufferedWriter() opens a writer for that file.
  3. .use { writer -> ... } starts a managed block.
  4. Inside the block, writer.write("Line 1") writes text to the file.
  5. The block ends.
  6. Kotlin automatically closes writer.
  7. println("Done") runs after the resource has already been closed.

If an exception happens

java.io.File("notes.txt").bufferedWriter().use { writer ->
    writer.write("Before error")
    error("Something failed")
}

Execution flow:

  1. The writer is opened.
  2. "Before error" is written.
  3. error(...) throws an exception.
  4. use() still closes the writer.
  5. The exception continues unless you catch it outside.

Example with try/catch around use():

try {
    java.io.File("notes.txt").bufferedWriter().use { writer ->
        writer.write("Before error")
        error("Something failed")
    }
} catch (e: IllegalStateException) {
    println("Caught: ${e.message}")
}

This is the normal Kotlin pattern when you want both:

  • automatic closing
  • explicit exception handling

Real World Use Cases

Resource management appears in many common programming tasks.

File handling

java.io.File("report.txt").inputStream().use { input ->
    val bytes = input.readBytes()
    println(bytes.size)
}

Used for:

  • reading config files
  • writing logs
  • exporting reports

Network I/O

val connection = java.net.URL("https://example.com").openConnection()
connection.getInputStream().use { input ->
    val text = input.bufferedReader().readText()
    println(text)
}

Used for:

  • API calls
  • downloading content
  • reading remote data streams

Working with sockets

Resources such as socket streams must be closed even if requests fail.

Database and storage APIs

Some APIs expose closable cursors, streams, or readers. use() helps ensure they are always cleaned up.

Android and server apps

Common examples include:

  • reading app assets
  • handling uploaded files
  • streaming HTTP responses
  • writing temporary files
Take Quiz

Real Codebase Usage

In real Kotlin projects, developers usually combine use() with a few common patterns.

1. Guard clauses before opening resources

fun saveText(path: String?, text: String) {
    if (path.isNullOrBlank()) return

    java.io.File(path).bufferedWriter().use { writer ->
        writer.write(text)
    }
}

This avoids opening a resource if input is invalid.

2. try/catch around use()

fun readConfig(path: String): String? {
    return try {
        java.io.File(path).bufferedReader().use { it.readText() }
    } catch (e: java.io.IOException) {
        null
    }
}

This is common for file and network operations.

3. Nested use() for multiple resources

java.io.FileInputStream("input.txt").use { input ->
    java.io.FileOutputStream("output.txt").use { output ->
        input.copyTo(output)
    }
}

This is the usual Kotlin replacement for Java's multiple-resource try-with-resources.

4. Expression style

Kotlin developers often keep code compact:

val text = java.io.File("data.txt").bufferedReader().use { it.readText() }

5. Encapsulating resource usage inside helper functions

fun writeLog(message: String) {
    java.io.File("app.log").appendText(message + "\n")
}

In larger codebases, low-level resource handling is often hidden behind reusable functions or services.

Common Mistakes

Mistake 1: Trying to use Java syntax directly

Broken code:

try (writer = OutputStreamWriter(r.getOutputStream())) {
    writer.write("Hello")
}

Why it fails:

  • Kotlin does not support Java's try (...) syntax.

Use this instead:

OutputStreamWriter(r.getOutputStream()).use { writer ->
    writer.write("Hello")
}

Mistake 2: Opening a resource and forgetting to close it

Broken code:

val writer = OutputStreamWriter(r.getOutputStream())
writer.write("Hello")

Problem:

  • the writer may remain open
  • buffered data may not be fully written

Better:

OutputStreamWriter(r.getOutputStream()).use { writer ->
    writer.write("Hello")
}

Mistake 3: Using the resource outside the use() block

Broken code:

val writer = OutputStreamWriter(r.getOutputStream())
writer.use {
    it.write("Hello")
}
writer.write("More text")

Problem:

  • writer is already closed after use() ends

Mistake 4: Assuming use() catches exceptions

use() closes resources automatically, but it does not swallow exceptions.

try {
    OutputStreamWriter(r.getOutputStream()).use { writer ->
        writer.write("Hello")
        error("Boom")
    }
} catch (e: IllegalStateException) {
    println("Handled")
}

Mistake 5: Forgetting that nested resources may both need management

If you manually create multiple closable objects, each may need its own use() block.

input.use { i ->
    output.use { o ->
        i.copyTo(o)
    }
}

Comparisons

ConceptJavaKotlinNotes
Automatic resource closingtry (resource) {}resource.use {}Same goal, different syntax
Part of language syntax?YesNoKotlin uses a library function
Exception handlingcatch / finallytry/catch/finally around use()Often combined with use()
Multiple resourcestry (a; b) {}nested use() blocksKotlin usually nests them

use() vs manual try/finally

ApproachExampleWhen to use
use()reader.use { ... }Preferred for closable resources
try/finallytry { ... } finally { reader.close() }Useful when custom cleanup logic is needed

Manual version:

val reader = java.io.File("data.txt").bufferedReader()
try {
    println(reader.readLine())
} finally {
    reader.close()
}

Idiomatic Kotlin version:

java.io.File("data.txt").bufferedReader().use { reader ->
    println(reader.readLine())
}
Take Quiz

Cheat Sheet

// Kotlin equivalent of Java try-with-resources
resource.use { value ->
    // use value
}

Key rules

  • Kotlin does not support try (resource) {} syntax.
  • Use use() for closable resources.
  • The resource is closed automatically after the block.
  • The resource is also closed if an exception is thrown.
  • If you want to handle exceptions, wrap use() in try/catch.

Common patterns

File("a.txt").bufferedReader().use { it.readText() }
File("a.txt").bufferedWriter().use { it.write("Hello") }
try {
    File("a.txt").bufferedReader().use { println(it.readLine()) }
} catch (e: IOException) {
    println("Read failed")
}
input.use { i ->
    output.use { o ->
        i.copyTo(o)
    }
}

Remember

  • use() is the idiomatic Kotlin replacement for Java try-with-resources.
  • Do not use a resource after its use() block ends.

FAQ

Is there a try-with-resources statement in Kotlin?

No. Kotlin does not have Java's try (...) {} syntax. The usual replacement is use().

What is the Kotlin equivalent of try-with-resources?

The idiomatic equivalent is calling use() on a closable resource:

File("data.txt").bufferedReader().use { it.readText() }

Does use() close the resource if an exception happens?

Yes. use() closes the resource whether the block succeeds or throws an exception.

Can I still use try/catch with use()?

Yes. Wrap the use() call in try/catch if you want to handle errors.

How do I manage two resources in Kotlin?

Usually by nesting use() blocks.

input.use { i ->
    output.use { o ->
        i.copyTo(o)
    }
}

Is use() better than manually calling close()?

Usually yes, because it is safer and less error-prone.

Can I use use() with files, streams, and writers?

Yes, as long as the object supports closing and is compatible with Kotlin's use() extension.

Take Quiz

Mini Project

Description

Build a small Kotlin program that writes text to a file and then reads it back safely using use(). This demonstrates the Kotlin way to manage resources without Java's try-with-resources syntax.

Goal

Create a program that saves a message to a file, reads the file contents, and prints them while ensuring all resources are automatically closed.

Requirements

  1. Create or open a text file.
  2. Write at least two lines into the file.
  3. Read the full contents back from the file.
  4. Print the contents to the console.
  5. Use use() for both writing and reading.
Take Quiz

Keep learning

Related questions

View more