Preserve cleanup and the original result
try can be an expression. catch handles selected failures, while finally runs when leaving the construct, including ordinary returns and exceptions. Avoid returning or throwing from finally, which can replace the original outcome.
fun countOrNull(text: String): Int? = try {
text.toInt()
} catch (failure: NumberFormatException) {
null
}
For JVM closeable resources, prefer use to manually pairing open and close operations:
fun firstLine(file: java.io.File): String? =
file.bufferedReader().use { reader -> reader.readLine() }
use closes the resource even when reading fails. A null first line means an empty file; a file-access failure still throws. Keep those outcomes distinct.
Exercise
Create a temporary file with one line, verify the result, then test an empty file and a missing path. Add a fake closeable that records closure and throws during work.
Check: cleanup should happen on success and failure without converting a real read error into an empty-file result.