Hi, new to kotlin here. I just stumble with managing resource with exception.
When doing IO operation, IOException is just normal and expected (internet connection closed suddenly, disk full, etc), so it must caught and properly handled.
In Java we can use try with resource to make a method never throwIOException and resource will properly closed when the method return.
Simplified example in Java
public ParseResult parse(File file) {
try (InputStream stream = new FileInputStream(file)) {
// do something with stream, might throw IOExcpetion here
return ParseResult.success(...);
} catch(IOException e) {
return ParseResult.failed(e);
}
}
Same functionality in kotlin that I can come up looks more verbose, and somehow uglier because deeper level of {}
fun parse(file: File): ParseResult {
return try {
file.inputStream().use {
// do something with stream, might throw IOExcpetion here
ParseResult.success(...)
}
} catch(e: IOException) {
ParseResult.failed(e)
}
}
Are there more concise and idiomatic way to manage resource that might throw exception in kotlin? Am I missing something?
But in that case, there is no more concise syntax. That should not be a problem though, because it is already very concise (the only things that are “redundant” are the 2 keywords and some braces), and it is usually not used in a lot of places.
If you do use it a lot, you can write an extension function that contains the use invocation and try-catch statement.
If you do use it a lot, you can write an extension function that contains the use invocation and try-catch statement.
Hmm… thanks for your suggestion. I will consider that option.
I thought something similar to try-with-resource already exists in the standard library.
edit
I have tried using extension function and ended up creating multiple extension function for File, URL, etc, which is IMHO not flexible enough (or maybe I just don’t know proper way to use it)
I am experimenting with functional try idea above, end up with idea of LazyTry
class LazyTry<C: Closeable>(private val create: () -> C) {
fun <T> use(transform: (C) -> T): Result<T> {
return try {
create().use { transform(it) }.let { Result.success(it) }
} catch (e: IOException) {
Result.failed(e)
}
}
}
It will lazily open the resource, at the time LazyTry::use() method invoked.
And using it will look something like this
LazyTry { file.inputStream() }.use {
it.reader().readText()
}.onSuccess {
// consume the result when success
println(it)
}.onFailed {
// handle the exception when failed
logger.info("failed to read file", it)
}
Resource will be closed automatically and exception will be caught, so it mimic Java try with resource.
I don’t like it, because you seem to be unable to write something like
val foo = LazyTry{…}
Also it would be better if this was a function call rather than a constructor call (lower case) because you don’t really want to be aware of the internal creation of the Result object.
Also needs a finally block.
I feel this really needs a language feature and all the attempts here are crude workarounds.
Each time use invoked, the InputStream will be created and closed when the use block end
Also it would be better if this was a function call rather than a constructor call (lower case)
Yes, good point. Maybe top level function like this will do
fun <C: Closeable> tryWith(create: () -> C) = LazyTry(create)
Then, my original question can be rewritten in kotlin like this
fun parse(file: File) : Result {
return tryWith {
file.inputStream()
}.use {
doSomethingWith(it)
}
}
Also needs a finally block.
As far as I understand, the purpose of finally is to clean up resources, which is already handled by Closeable.use extension method
I feel this really needs a language feature and all the attempts here are crude workarounds.
Totally agree with you! Kotlin is supposed to be “Better Java” (or so I heard). This problem already solved since Java 7!. Too bad it is not implemented yet.
As for the finally block I often found that you need to do something in addition to closing the Closable object, like release a ReadWriteLock as in the example or log a message or whatever. Don’t have a specific example right now but I found it very handy to be able to use try-with-resources with finally in Java.