1. Introduction
In Kotlin, there are several ways to handle errors or exceptions. Two common approaches are the try / finally expression and the runCatching() lambda. This tutorial will explain the similarities and differences between these two methods.
2. Similarities in Source Code from Kotlin Standard Library
Both try / finally and runCatching() are provided by Kotlin itself, the first being part of the language and the second being part of the standard library. They can be used to handle exceptions that occur during code execution. Let’s start with an example of try / finally:
fun tryToDoSomething(): Int {
var result = 0
val resource = acquireResource()
try {
// Code that can throw an exception
result = performAction(resource)
} finally {
releaseResource(resource)
}
return result
}
The above code acquires a resource, performs an action that can throw an exception, and then releases the resource in the finally block. Let’s write the equivalent code using runCatching():
fun doSomethingRunCatching(): Int {
val resource = acquireResource()
val result = runCatching {
// Code that can throw an exception
performAction(resource)
}
releaseResource(resource)
return result.getOrThrow()
}
In this case, we use runCatching to wrap the code that can throw an exception. The resource is acquired before the lambda is passed to runCatching() and then released after it in a separate line. The result is returned using the getOrThrow() function, which either returns the value or throws the exception. In both our examples, we’re forcing the exception to be thrown instead of handling it.
3. Edge Cases
While try / finally and runCatching() have some similarities, there are some edge cases where they behave differently. One important difference is that try / finally guarantees that the finally block is always executed, regardless of whether an exception is thrown or not.
We do not have the same type of flow control with runCatching(). Our only option is to manually release resources after a runCatching() block. *If an exception is thrown from our runCatching() by using getOrThrow(), or even after this block but before we have released our resource, the resource will not be released.*
4. Choosing Between These Options
When choosing between try / finally and runCatching(), it’s important to consider the specific use case. If the code requires resource cleanup without handling any exceptions, then try / finally is the better option. If all exceptions must be caught and handled, then runCatching() can be a more concise option. Additionally, runCatching() is just a functional wrapper around try / catch. This difference allows us to decide based on whether we want to handle exceptions directly or perform some additional cleanup logic without handling any exceptions.
5. Conclusion
In conclusion, try / finally and runCatching() are both ways to handle exceptions in Kotlin. While they have some similarities, they also have some important differences, particularly in how they handle finally blocks and edge cases. It’s important to choose the option that best suits our specific use case. As always, the code for this article is available over on GitHub.