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 componentN 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?