Exceptions

In an ideal world, nothing would ever go wrong with our apps.

In an ideal world, the author of this book would have a full head of hair.

This is not an ideal world.

As with most modern programming languages, when things go wrong, exceptions get raised. We will need to be able to handle those exceptions when they occur, one way or another.

Kotlin’s exception system is very similar to that of Java, except that there are no checked exceptions.

Catching Exceptions

A classic scenario where our apps can fail is with a NullPointerException.

We will explore nullability in greater detail in an upcoming chapter, as Kotlin takes steps to try to minimize a NullPointerException. However, you can still wind up crashing if you try accessing null inappropriately, such as we are doing here:

  var thisIsReallyNull: String? = null

  println(thisIsReallyNull)
  println(thisIsReallyNull!!.length)

This compiles fine, but it crashes at runtime with a NullPointerException. We will see exactly what ? and !! do in the chapter on nullability.

For now, though, let’s focus on the fact that we are getting an exception. In this case, the exception is very artificial, but there are many places where an exception may be expected somewhat. For example, if you try accessing the Internet, there may be all sorts of problems resulting in all sorts of exceptions.

Traditional Try-Catch

Kotlin supports try and catch, using syntax very similar to that of Java:

  try {
    var thisIsReallyNull: String? = null

    println(thisIsReallyNull)
    println(thisIsReallyNull!!.length)
  }
  catch (e: Exception) {
    println("ERROR: '$e'")
  }

To catch an exception, you:

Here, we wrap the null reference in the try block. If — or, rather, when — that code throws an exception, we will get control in our catch block, where we can do something.

Between the catch keyword and the block is a parameter-style declaration in parentheses, indicating:

Here, anything that extends from Exception would be caught and handled by our catch block.

Cascading Catches

If you want to handle different types of exceptions differently, you can have multiple catch blocks:

  try {
    var thisIsReallyNull: String? = null

    println(thisIsReallyNull)
    println(thisIsReallyNull!!.length)
  }
  catch (npe: NullPointerException) {
    println("You tried doing something unfortunate with null. Stop that.")
  }
  catch (e: Exception) {
    println("ERROR: '$e'")
  }

Here, when the null reference throws an NullPointerException, we will execute the first catch block. Otherwise, if it were to throw some other type of Exception, we will execute the second catch block.

Note, though, that Kotlin does not support multiple types in a single catch, the way you can in Java:

try {
  // something that might throw multiple types of exceptions
}
catch (ThisException | ThatException ex) {
  // handle those two exception types
}

What You Might Catch

The details of what exceptions can be thrown will vary by circumstance. In particular, the Kotlin environment will play a major role here. For example, java.lang.ArithmeticException is a Java thing that you might catch on Kotlin/JVM. Other Kotlin environments, such as Kotlin/JS, will not know what java.lang.ArithmeticException is.

Even within an environment, the details may vary. For example, in Java, Exception is a subclass of Throwable. An Exception is something that the Java language designers felt that apps should be able to handle. An Error, though, is another type of a Throwable that the language designers considered to be unrecoverable. For example, an OutOfMemoryError is something that the Java designers thought was something that should result in the death of your process.

Typically, in a catch block, you identify exceptions that you want to catch because you have specific local ways to recover from it. If you need a true “catch everything” catch block, in Kotlin/JVM, you would catch a Throwable, not an Exception.

A Missed Catch

An exception that is not caught cascades up the “call stack” to whatever the next function is:

fun itsGonnaBlow() {
  var thisIsReallyNull: String? = null

  println(thisIsReallyNull)
  println(thisIsReallyNull!!.length)
}

fun main() {
  try {
    itsGonnaBlow()
  }
  catch (npe: NullPointerException) {
    println("You tried doing something unfortunate with null. Stop that.")
  }
  catch (e: Exception) {
    println("ERROR: '$e'")
  }
}

Here, itsGonnaBlow() triggers the exception, but we do not have a try/catch structure in that function. So, when itsGonnaBlow() throws the exception, it gets passed to whoever called itsGonnaBlow(), and so on.

Try as an Expression

Like if and when, try (with its associated catch blocks) represents an expression, and you can use its result as you would any other expression, such as assigning it to a variable or property.

In the case of if and when, the result of the expression is based on which of the branches you take. In the case of try, the result of the expression is:

fun lengthifier(nullAllowedButNotReally: String?): Int {
  return nullAllowedButNotReally!!.length
}

fun main() {
  val result = try {
    lengthifier(null)
  }
  catch (e: Exception) {
    -1
  }

  println(result)
}

Here, we compute the length of a String in a function, and we wrap that function call in a try/catch structure. result will either be:

In this case, we are trying to get the length of null, so we get -1 as the result of our try expression.

Finally, Don’t Forget finally

As with Java, Kotlin also offers a finally block that you can chain onto the end of your try/catch structure. This block’s code will be executed either:

Typically, you use the finally block to ensure that everything is cleaned up from whatever may (or may not) have happened in the try:

try {
  // TODO something that might blow up
}
catch (e: Exception) {
  // TODO something to deal with the blow up when it blows up
}
finally {
  // TODO clean up afterwards
}

Note that if you use the try as an expression, the finally block has no impact on the result of the expression. The result will still be based on the try result or the catch result — the finally code is executed but does not change the result.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.