I must admit I quite like try-with-resources which was introduced in Java 7. I tried to find what the Kotlin way is, but with just little success, nevertheless I found an article on this topic:
http://blog.jonnyzzz.name/2013/12/try-with-resource-in-java.html
The article is quite interesting. The points about the solution that Java chose are notable, unfortunately the solution for Kotlin is not satisfying at all, I would say, because it can’t handle even the use cases where the Java solution works very well (i.e., safe resource allocation and disposal, especially with multiple resources). Maybe I just don’t see something, being a beginner, but I guess that there must be some support from the compiler to achieve at least the same level of usability and consistency. The main pain point of the solution from the article is that it needs an initialized variable before the
using
function and it can’t cope safely with multiple resources at once, which is IMO one of the main selling points of the Java solution. Multiple resources are difficult to handle with just simple try-finally.
I’d be happy if Kotlin had a construct for safe resource acquisition and release that would be at least as good as what Java provides. If I may, I’d like to share an idea/proposal of such a construct, using some examples:
Firstly, the basic case, similar to Java solution: opens two streams, assigns the objects into local variables (I guess that
val
can be omitted, so just the name of the variable is good enough):
using (is = newInputStream(“file.txt”), os = newOutputStream(“copy.txt”)) { // If os fails, is would be closed anyway
// copy the file content
} // Closes all acquired resources
Here I assume that the resource has a method of a well-known name, e.g.
close
to reuse
AutoCloseable
directly (it is similar to the solution used for tuples with the
component
N
methods). But sometimes the method might be missing or some different handling is needed. Therefore the more general form might be available:
using (original = setThreadContextClassLoader(desired) with { setThreadContextClassLoader(original) }) {
// Some code
The
with
keyword (or any other suitable, for instance recycling lengthy
finally
) allows specifying custom cleanup. In this simple case with just one resource, it does not bring much more than simple try-finally, besides the local variable declaration. Still, there are things to improve. One of the deficiencies of the Java way, according to the article, is when optional catch and finally blocks are executed (after the clean up code). What if the
using
clause could be combined with
catch
and
finally
, too?
using (context = makeContext()) try {
// Some code
} catch (e: IllegalStateException) {
// Resolved before closing context, so we can use it here
} finally {
// Optional, like the catch, and again before closing the context
In the case that the catch/finally should apply to exceptions thrown by the resource acquisition or release, just change the order:
try using (context = makeContext()) {
// Some code
} catch (e: IllegalStateException) {
// Resolved after closing context, so we can’t use it here
} finally {
// Optional, like the catch, and again after closing the context
Well, this latter case is just sugar and the same thing could be achieved with extra parenthesis around the
using
part. But this syntax saves one nesting level and makes the nesting consistent with the former case.
Another point is that the name of the variable might be omitted if the default handling (using
close
method) is good enough and the variable is not used otherwise, because just the allocation and release side effects are important:
using (logScope(“Operation A”)) {
// Some code executes
} // Here the object created by logScope function is released
A note for the end: some of the cases look simple at the first glance. But try to think over following: what happens if the allocation throws and exception? What if there are more resources and any of them may thrown an exception? What happens when a release of any resource throws an exception? Or when a resource allocation returns null? And we always want that all allocated resources will be released, no matter what happened. And of course, it would be nice if no exception is lost – using suppressed exceptions from Java 7 is definitely worth that. Taking all this into account, I believe that it is the job for compiler to generate the proper handling.
What are your opinions?
Petr,
Thanks for such a detailed post! We are definitely looking into try-with-resource pattern but we don’t want to add special constructs to the language until absolutely necessary. So, we are experimenting with constructs like this:
val text = using {
val is = InputStream(…).autoClose()
val os = OutputStream(…).autoClose()
“text”
using introduces “ResourceContext” and autoClose adds items into it. In finally block inside “using” it closes them in reverse order. So, if OutputStream throws, InputStream will still be destroyed.
there could be variants like this:
using(resource1, resource2) {
} // when you already have resources
InputStream(…).using { stream -> … } // for a shorter syntax with one resource
This is being developed right now with the rest of IO part of stdlib, stay tuned!
Great :) I'm looking forward to have the possibility to manage resources safely. Safety first ;)
I’m curious how the resource context would work. Obviously, I don’t know Kotlin well enough to see the way how to provide an instance of the resource context (assuming it is just a library class with some hooks called by the
autoClose
and
using
functions) to invocations of
autoClose
within the appropriate scope. Knowing this technique would be… hm, inspiring, I would say. For example, the same technique could, for instance, support explicit library locks (safe lock-unlock) in more complex deadlock-preventing locking scenarios.
I assume that the facility can cope with exceptions thrown by the resource release function or the
using
block can be nested: for instance I want split a file into several other files sequentially, so I have a
using
block for the source source, a loop in the block and in the loop again a nested
using
block for the target file.
Perhaps yet one more question, remotely related: Java 7 introduced suppressed exceptions, which works exceptionally well with try-with-resources. Will that be supported in Kotlin as well?
I will definitely stay tuned!
Basically the technique is like this (writing code right in the post, so forgive possible typos)
class ResourceContext : Closeable {
val closeables = arrayListOf<Closeable>()
fun <T:Closeable> T.autoClose() : T { // yes, instance function with receiver
closeables.add(this) // "this" is receiver, not class. "this@ResourceContext" would be class instance.
return this
fun close() {
closeables.reverse().forEach { it.close() }
inline fun using(body: ResourceContext.()->Unit) {
val context = ResouceContext()
try {
context.body() // body is function with receiver, so we call it on ResouceContet instance
finally {
context.close()
fun fn() {
using { // this block is lambda passed into body parameter.
// "this" inside this lambda is "ResouceContext", because it is lambda with receiver.
val closeable = InputStream(...).autoClose() // calls autoClose() on lambda's this, passes InputStream as receiver.
I see :O The instance function with the receiver is the trick I didn't know. Hm, that's smart. I can now imagine how it can be integrated with decent exception handling or how to use it for other cases, like the locks. And it seems that it there is no problem with nesting either.
Thank you for the explanation 