Kotlin: Getting to knows with Exceptions

Mochamad Iqbal Dwi Cahyo
Staffinc Tech
Published in
8 min readSep 29, 2021

Problems: Exception Handling

Exceptions were something that occurred on daily basis. In Java, they can eliminate the problems we might find on a specific operation for every programming language (back then), see this example:

Referenced from: https://elizarov.medium.com/kotlin-and-exceptions-8062f589d07

Writing the above handling seems too much error-prone if there’s no proper linter (pipeline) and a strict code review process.

An alternative: Throw Exception

Java then introduce a keyword called throws, as a compiler checks that we need to check or handle it because this unique concept of checked exceptions would solve the error-prone of error-handling using a manual if condition with try/catch but that's not the case exception designed in Kotlin.

Checked exceptions are something to guard our codebase so we are recommended to catch that particular method when invoked, e.g:

References from: https://docs.oracle.com/javase/7/docs/api/java/lang/Integer.html#parseInt(java.lang.String)

The above was an example of a checked exception allowing us to have the compiler checks with try/catch for example:

Compiler checks

If we didn’t handle the checked exceptions on method .read it'll show a compiler error checks

Warning checks

This code still compiled & runs, but might produce NPE in the runtime

The above approach was still something tedious to write especially the anti-pattern for this simple exception

We’ll not cover up the gritty details about the above anti-pattern but you can see here for details:

TL;DR: The anti-pattern stated that we should catch only the relevant exception so we know for sure what to handle, instead of catch the general class Exception that will throw any possible (almost everything) that (un)intended as well

Multicatch exist!

Prior to Java 7, this is not possible, onwards now we can write a simple “or” | for the exception, see here for details:

Let’s see the sneak-peek of what it looks like both in Java & Kotlin for the multi-catch exception:

Well, in Java we had what we called a multi-catch exception using a simple | symbol and voila, it's more readable but how about in Kotlin?

Unfortunately the Kotlin by design it’s not desired to have a multi-catch exception as this ticket is shown:

https://youtrack.jetbrains.com/issue/KT-7128 (still opened from 6yr ago)

And we’re ended up abusing every possible exception (see below screenshot)

The difference with Kotlin approach in Sampingan codebase

Yes the above is our codebase approach back then :) when handling the TUS protocol because its variety of possible exception thrown inside the try statement :(

There’s a follow-up statement regarding the multi-catch in the following resources that recommended to watch on the weekends

There’s also a discussion on kotlinlang.slack.com regarding the multicatch

Later, we modify our code to be more readable with an alternative of multi-catch exception, and inspired by the community to have this extension instead:

Referenced here: https://stackoverflow.com/a/49407498/3763032

What are the problems anyway?

We solve most of our exception problems back then with the above nice little extension for a while, until we use a heavy lambda action & coroutines (and yes we use Streams, Executors & Threads in our Java code as well).

Psssttt don’t tell anyone, we also invested heavily on building an interopability like suspend function to be called in Java :)

Our Kotlin codes:

Referenced from: https://stackoverflow.com/a/60613645/3763032

Also, many more like making our Java method helper become deprecated and called the Kotlin code inside it. Interoperability in Kotlin comes at a price such as the above condition for suspend function and many annotations we used such as JvmOverloads, JvmName, JvmStatic, & etc

/**
*
* @deprecated will be removed at vx.y.z
*
**/
@Deprecated
public static String doSomething() {

return OurKotlinCodeKt.doSomething();
}

Why the exception is not mixed well in the lambda? Let’s see the following examples:

Referenced here: https://www.baeldung.com/java-lambda-exceptions

The above code inside .forEach might be problems when there is0 value in the list and throw the ArithmeticException: / by zero. That's bad!

Even we modify our code to have an exception like this

But that’s another cumbersome code to write :) also, we’re losing the conciseness of why we using lambda on the first place.

Writing a wrapper for the try catch also convenient but that’s another tech debt to introduce :))) as shown in here from Baeldung

More examples have shown how much boilerplate to write with a lambda function that returns simply checked exception as explained details in this articles here from DZone:

tl;dr a wrapper workaround: https://stackoverflow.com/a/18198349/3763032

So how Kotlin Design its Exception?

The concept is the same with Java checked exception as a return value of functions with an annotation called @Throws see here from official Kotlin docs

Kotlin also aware of pre-conditions (alternatively in Java we can use Guava) turns out Kotlin has this internally in their SDK

A precondition is a mechanism to throw an exception that we unable to detect at compile time

e.g require(Boolean) {}:

Referenced from: https://elizarov.medium.com/kotlin-and-exceptions-8062f589d07#:~:text=handling%20program%20logic

TL;DR:

Checked Exceptions:

@Throws(NullPointerException::class) fun doSomething()

We need to handle it before compilation otherwise it’ll show a compile-error

Preconditions:

To check runtime or program (or logic) error such as the above snippet using require(Boolean) {}

Kotlin Contract (compile-time): This is require a separate discussion, but worth to read how Kotlin allowing us to write the extension or method that’s has some kind of linter to fails the compile. See here:

We are recommended to avoid code smells in our Kotlin code as stated by Roman Elizarov here:

https://elizarov.medium.com/kotlin-and-exceptions-8062f589d07

Dual-use APIs

You might familiar with this kind of extensions in Kotlin, such as:

Why Kotlin bother to create a different method just for the sake of nullability?

Well, it’s for us (developer) to reduce writing thistry/catch style:

Alternatively, we can use.toIntOrNull() and our code now is more (opinionated) readable:

From a list or collection & map has their own extensions of a dual-use APIs for our conveniences e.g: getOrNull!

The above concept was introduced to leverage the nullability features in Kotlin there’s a supportive statement why null is not a foe or such-called a billion-dollar mistakes (but a friend), See here:

Also, Kotlin has the power of elvis operator ?: that really convenient to be used when dealing with nullability as the above example.

You might be curious why Kotlin doesn’t have the ternary operator as well, e.g: (true) ? "yes" : "no".

Here’s why the Kotlin team decided not to have a ternary operator like in Java:

Or go to this Kotlin discussions:

Designing the API

For us when writing the code, whether we ships and SDK to be consumed by fellow developers or internally, we need to bear in mind that:

  1. Use exception for logic errors
  2. Type-safe results for everything else
  3. If other than logic error, write a wrapper to convert exceptions to the desired return values

Therefore our caller will be freed to such length from handling the old try/catch! :(

P.S.:

  1. Is it a single error condition: success, or failure? Use null as a failure and a type-safe result for success
  2. If it a multiple error condition, take a bit of time to write a wrapper (explained in more details later)

We define a sealed class for the type-safe result of ParseException instead, a simpler failure to return null, because we (for example) wants to return the errorOfset.

Why sealed class? the result of .parse and .errorOfset was returning a different data type, therefore we wrap it as a sealed class instead.

This is might be a rare case, but as our guidelines for the multiple error conditions or a different return values for failure cases

Input or Outputs failures

As previously stated about require(Boolean) {} this is a good precondition so that the caller side can have a more centralized (self-documenting) code that we need to avoid adding the negative values for it, e.g:

Referenced from: https://elizarov.medium.com/kotlin-and-exceptions-8062f589d07

Asynchronous and Coroutines Exceptions

In Kotlin this is by principles is a first-class citizen supported for the coroutineScope & launch that indicate cancellation or failures (internally use the checked exceptions)

See full details about exceptions in coroutines here:

There’s a handy extension called .runCatching especially for coroutine launch or async (if you prefer a functional way rather than using try-catch) and for the Kotlin Flow we can also be using .catch operator to handle the exceptions.

E.g for runCatching from the above link:

viewModelScope.launch {
kotlin.runCatching { repository.getNecessaryData(this) }
.onSuccess { liveData.postValue(ViewState.Success(it)) }
.onFailure { liveData.postValue(ViewState.Error(it)) }
}

Thanks for the correction and feedback Ade Dyas

There’s also a worth mentioning article about the Exception in Kotlin here (the benefits of using runCatching):

Kotlin coroutines error handling strategy — `runCatching` and `Result` class | by Hossain Khan | May, 2021 | Medium

Handling the exception with on how we able to provide default value, a fallback or even recover from an error such as following:

getOrDefault or getOrNull, we can provide null or default value

fold, we could mapping or transform the return value into a different data type:

.fold(onSuccess = { success: String -> success.toIntOrNull() }, onFailure = { error: Throwable -> error.message })

Bonus

Agreed with Erik Huizinga statement regarding the powerful .runCatching and we might ended-up abusing the thrown exception and therefore this article also address the multi-error values or

In general, try to avoid catching all exceptions just for the simplicity of this runCatching… | by Erik Huizinga | May, 2021 | Medium

https://erikhuizinga.medium.com/in-general-try-to-avoid-catching-all-exceptions-just-for-the-simplicity-of-this-runcatching-2a1b4bdc8b2a

It’s also recommended to catch up with the Kotlin community on a slack, youtrack & Github issue for examples:

  1. https://github.com/Kotlin/kotlinx.coroutines/issues/1147 (this is where the coroutines discussion takes places, or we can check for: github.com/Kotlin/<library>/issues)
  2. https://twitter.com/kotlin/status/1022114553302331392?s=20 (kotlinlang.slack.com) we can find many Kotlin library here (also: #android, #squarelibraries, #kmm, #compose, & etc)
  3. If wants to know more about the Kotlin design and enhancement we can star this repo for sure: https://github.com/Kotlin/KEEP
  4. A curated Kotlin newsletter: http://www.kotlinweekly.net/

Originally published at http://github.com.

--

--

Mochamad Iqbal Dwi Cahyo
Staffinc Tech

A geek enthusiast. Sometimes photographer, culinary seeker or traveler. He ready to craft ideas!