Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Features of Exceptions

Exceptions have two key features that distinguish them from the approaches considered previously.

First, an exception is an object of a distinct type, used specifically to signal errors1. The type of the exception conveys information about the nature of the error, and further details can be provided via data stored inside the exception object.

Second, an exception cannot be ignored. When an exception occurs, it fundamentally changes the flow of control within a program. If no code is found to intercept and deal with an exception, it will halt the program.

Information Transfer

Exceptions are implemented as classes, with names that indicate the kind of error that they represent. These classes are typically organized into a hierarchy:

---
config:
  class:
    hideEmptyMembersBox: true
---
classDiagram
  Exception <|-- RuntimeException
  RuntimeException <|-- ArithmeticException
  RuntimeException <|-- IllegalArgumentException
  RuntimeException <|-- IndexOutOfBoundsException
  IllegalArgumentException <|-- NumberFormatException

Note that classes lower in the hierarchy represent more specialized types of error.

Aside from conveying information via the name of its class, an exception has a String property named message that can be used to give further details of the error. This property can be given a value when you create an exception object.

For example, the variance() function discussed previously could signal an insufficient amount of data using this exception object:

IllegalArgumentException("not enough data")

The class name tells us that the argument supplied to variance() is not suitable, and the string provides details of why that is the case.

The standard exception types will often meet your needs, but sometimes it is useful define your own exception class, and Kotlin makes this very easy to do:

class MyException(message: String) : Exception(message)

There are basically three reasons why you might want to do this:

  • You want to convey more information about the error, via the name of the class
  • You want to be able to easily intercept this specific type of exception
  • You want to add more information to the exception, beyond a basic error message

For example, consider the writeToFile() function discussed earlier. If writing to the file fails, you might want to indicate this via a dedicated exception that includes the line number on which failure occurred. You could define a suitable exception class like this:

class TextException(msg: String, val line: Int) : Exception(msg)

Note

Don’t worry about understanding the syntax here. We will discuss it properly when we cover classes and inheritance later.

Flow Of Control

  1. Open the file Control.kt, which is in the tasks/task9_2 subdirectory of your repository. Study the code, then compile and run the program.

    Examine the output. This shows you that main() calls the function first(), which in turn calls second(). This stack of function calls then ‘unwinds’: second() finishes normally, then first(), then main().

  2. A line of second() that causes an exception has been commented out. Uncomment it now, then recompile the program and run it again. How has the behaviour changed?

The key point to note from this demo is that none of the messages about leaving a function are printed. When the exception occurs inside second(), it prevents any of the other code from running2. The only way to regain control over program execution is to catch the exception.

The additional information displayed here is known as a stack trace. It shows details of the call stack at the time of the exception, and is very useful for debugging.

Tip

When you see a stack trace like this, work your way down from the top, moving pass any references to library code in which the exception may have originated, until you reach the first reference to your own code. This will give you a filename and a line number, showing you where you can begin your investigation into what caused the error.


  1. This is not true of all languages. In C++, for example, it is possible to use values of almost any type as exceptions.

  2. This is true for this specific example, but other code can run in cases where finally has been used.