In general should we prefer using simple try-catch...
# codereview
g
In general should we prefer using simple try-catch pattern instead of
runCatching
in cases where we care about only one type of exception or is this too much neatpicking? For example:
Copy code
val result = runCatching { transaction() }
return result.getOrElse {
    if (it is IllegalArgumentException) return null else throw it
}
vs
Copy code
return try {
    transaction()
} catch (e: IllegalArgumentException) {
    null
}
Thanks in advance for any answers !
Not sure if we should care that much about boxing in
Result.kt
type since it is a value class.
p
I would definitely use try/catch over
runCatching
if you only care about a single type of exception
j
In general, catch as few exceptions as possible. If you care about something specific, then just catch that, otherwise you may end up catching unexpected things and this could hide bugs. Even if you did want to catch "all exceptions", you shouldn't use
runCatching
because it also catches errors (
StackOverflowError
,
NoClassDefFoundError
,
OutOfMemoryError
, and the likes), which you really don't want to catch most of the time.
Now another point, you should not want to catch
IllegalArgumentException
. This exception is generally used to indicate a bug in your code (like a function used inappropriately). You should instead fix the code that generates this exception, and not catch at all.
e
I would argue that the only good use for
Result
(and
runCatching
by extension) is when you are always definitely going to rethrow that exception. this is true for all the coroutines machinery that uses
Result
internally, but rarely for any non-framework code and definitely not here
catching everything is simply far too dangerous on JVM, you don't know a priori what can be handled safely and what can't. you must only handle what you know
in this case… IAE is for verifying prerequisites, ideally you have some safe way to do that beforehand or at least some function that uses
null
or some other signal instead of throwing. but if your API is poor, you may have no choice but to catch. in that case, do the second option
as a side note, even though
Result
is a value class, it does still box primitives and put extra wrapping around exceptions, because that's the only reasonable way for it to be implemented on JVM (at least until Valhalla)
g
First of all, thank you all for your answers ! • mb about the exception i had in mind more about the concept so i ended up with bad exception type 😛 • In runCatching implementation the code throws in else if it is not the expected Exception so when the code ends up like this:
Copy code
internal inline fun <T> runRecoveringTransaction(transaction: () -> T?): T? {
    val result = runCatching { transaction() }
    return result.getOrElse {
        if (it is DataIntegrityViolationException) return null else throw it
    }
}
Which throws in case of other exceptions should i refactor it? Also, if this code ends up in your code review, how much weight you would put for the refactoring to be made?
p
I would not pass this code it’s more verbose, slower, and harder to reason about
e
why go through the effort of catching and re-throwing when you can just catch only what you want and let everything else propagate normally
idiomatic Kotlin does try to avoid or minimize exceptions in general, but when they do need to be handled,
try
-
catch
is idiomatic
621 Views