An exception is an unwanted or unexpected event, which occurs during the execution of a program. In Kotlin, all exception classes are descendants of class Throwable. If exceptions are not handled, then It will throw an exception and will stop the execution of program.

Exceptions can occur due to many reasons like:

  • Whenever a user provides invalid data.
  • The file requested to be accessed does not exist in the system.
  • When the Java Virtual Machine (JVM) runs out of memory.
  • Network drops in the middle of communication.

1. Error vs Exception

ErrorException
Errors occur at runtime and not known to the compiler. All exceptions occurs at runtime but checked exceptions are known to compiler while unchecked are not.
Errors are defined in java.lang.Error package. Exceptions are  defined in java.lang.Exception package
Recovering from Error is not possible. We can recover from exceptions by using try-catch block

2. Exceptions in java vs kotlin

Kotlin’s most exceptions depend on java’s exceptions.

2.1 Exceptions in java

There are mainly two types of exceptions in Java as follows:

a. Checked Exceptions

These exceptions are checked at compile time. The program will not work if we haven’t handled the checked exception.
For example IOException, FileNotFoundException etc.

b. Unchecked Exceptions

These are not checked at compile time. Even if we don’t handle these exceptions, the program will compile successfully.
For example ArithmeticException, ArrayIndexOutOfBoundsException.

2.2 Exceptions in Kotlin

In Kotlin, There are no checked exceptions we have only unchecked exceptions and can be caught only at run time.

3. Exceptions in kotlin

The following are unchecked exceptions in kotlin programming language.

3.1 ArithmeticException

This exception occurs when an invalid arithmetic operation is performed. Example: dividing a number by zero.

// Cause the exception
fun main() {
    val calculateResult = 10 / 0
    println("Value is $calculateResult")
}

// Catch the exception
fun main() {
    try {
        val calculateResult = 10 / 0
        println(calculateResult)
    } catch (e: ArithmeticException) {
        println("Number cannot be divided by zero")
    }
}

3.2 ArrayIndexOutOfBounds

This exception occurs whenever we try to access any item of an array at an index which does not exist.

// Cause the exception
fun main() {
    val list = listOf(1, 2, 3)
    println(list[4])
}

// Catch the execption
fun main() {
    val list = listOf(1, 2, 3)
    try {
        println(list[4])
    } catch (e: ArrayIndexOutOfBoundsException) {
        println(e)
    }
}

3.3 IllegalArgumentExcepction

An IllegalArgumentException thrown to indicate that a method has been passed an illegal or inappropriate argument.

// Catch the exception
fun main() {
    val age = -21
    if (age < 0) {
        throw IllegalArgumentException("Age must be greater than 0")
    } else {
        println("The age you entered is $age")
    }
}

3.4 NumberFormatExecption

The NumberFormatException is thrown when we try to convert a invalid format string into a numeric value such as float or integer.

// Cause the exception
fun main() {
    // "Test" is not a number
    val number = "Test".toInt()
    println(number)
}

// Catch the exception
fun main() {
    try {
        // "Test" is not a number
        val number = "Test".toInt()
        println(number)
    } catch (e: NumberFormatException) {
        println("Number format exception + $e")
    }
}

3.5 NullPointerException

NullPointerException is thrown when we try to invoke a method or property on a null object.

// Cause the exception
fun main() {
    val name: String? = null
    println(name!![0])
}

// Catch the exception
fun main() {
    try {
        // null value
        val name: String? = null
        println(name!![0])
    } catch (e: NullPointerException) {
        println("Cause exception $e")
    }
}

3.6 IllegalStateException

IllegalStateException  is thrown when a method has been invoked at an illegal or inappropriate time.

// Cause the exception
fun main() {
    val numbers = arrayListOf(1, 2, 3)
    val iterator: MutableListIterator<Int> = numbers.listIterator()
    // Loops with while until it has next element in element
    while (iterator.hasNext()) {
        // Try to remove the element from array list.
        iterator.remove()
    }
}

// Catch the exception
fun main() {
    val numbers = arrayListOf(1, 2, 3)
    val iterator: MutableListIterator<Int> = numbers.listIterator()
    try {
        // Loops with while until it has next element in element
        while (iterator.hasNext()) {
            // Try to remove the element from array list.
            iterator.remove()
            // Throws exception at this point because you cannot remove element without first telling
            // the iterator to where it should move i.e., iterator.next() before iterator.remove()
        }
    } catch (e: IllegalStateException) {
        println("IllegalStateException")
    }
}

3.7 ClassCastException

class cast exception is thrown by when we try to cast an Object of one data type to another.

// Cause the exception
fun main() {
    val string: Any = "1"
    val resultValue = string as Int
    println(resultValue)
}

// Catch the exception
fun main() {
    val string: Any = "1"
    try {
        val resultValue = string as Int
        println(resultValue)
    } catch (e: ClassCastException) {
        println("Caused exception $e")
    }
}

3.8 UnsupportedOperationException

UnsupportedOperationException is a RuntimeException or say UncheckedException. This exception is thrown when an unsupported operation is attempted.

// Cause the exception
fun main() {
    val numbers: List<String> = listOf("one", "two", "three")
    val moreNumbers = numbers as MutableList<String>
    numbers.add("four")
    println(moreNumbers)
}

// Catch the exception
fun main() {
    try {
        // Can never add more elements
        val numbers: List<String> = listOf("one", "two", "three")
        val moreNumbers = numbers as MutableList<String>
        numbers.add("four")
        println(moreNumbers)
    } catch (e: UnsupportedOperationException) {
        print("Caused exception $e")
    }
}

3.9 NoSuchElementException

NoSuchElementexception is a Runtimeexception or say UncheckedException. This exception is thrown to indicate that there are no more elements in an enumeration.

// Cause the exception
fun main() {
    // Empty list of strings
    val animals = listOf<String>()
    val animalIterator = animals.iterator()
    animalIterator.next()
}

// Catch the exception
fun main() {
    try {
        // Empty list of strings
        val animals = listOf<String>()
        val animalIterator = animals.iterator()
        animalIterator.next()
    } catch (e: NoSuchElementException) {
        print("Caused exception $e")
    }
}

3.10 ConcurrentModificationException

ConcurrentModificationException is thrown by methods when they discover concurrent modification of object where such modifications are not permitted.

// Cause the exception
fun main() {
    // Adding elements
    val numbers = mutableListOf(1, 2, 3)
    // Looping through a list
    for (item in numbers) {
        // Trying make changes to that list
        numbers.removeAt(1)
    }
}

// Catch the exception
fun main() {
    try {
        // Adding mutable elements
        val numbers = mutableListOf(1, 2, 3)
        // Looping through a list
        for (item in numbers) {
            // Trying make changes to that list at same time
            numbers.removeAt(1)
        }
    } catch (e: ConcurrentModificationException) {
        print(e)
    }
}

4. Exception Handling

Exception handling in Kotlin can be understood using four keywords. They are:

  1. Try block contains the code that cause an exception. The try block cannot be used alone. The try block must be followed by at least one catch block or one finally block or both.
  2. Catch catches the exception thrown by the code in the try block. It is the block where the exception is actually handled. The catch block is placed after the try block. We can have multiple catch block after a try block.
  3. Finally block statements will always execute regardless of whether exception occurs in try block or not.
  4. Throw keyword is used to throw an exception explicitly. If developer wants to throw an exception in some situation, we can use throw keyword.

4.1 try & catch

The try and catch block are used to handle an exception. The code which may cause an exception is placed inside the try block and the exception thrown by try block is caught using catch block.

fun main() {
    //The syntax of try-catch block is:
    try {
        // The code which cause an exception
    } catch (e: Exception) {
        // Handling the exception 
    }
}

Let us first see a program which will throw an exception and what will happen if we don’t handle it:

fun main() {
    val a: Int = 10
    val b: Int = 0
    println("The division of $a and $b is: ${a/b}")
}

Message ->
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at MainKt.main(main.kt:4)
	at MainKt.main(main.kt)

The above code will throw an ArithmeticException because number cannot be divided by zero. The exception is not handled so it terminates the execution of program.

4.2 try & catch Expression

The unique feature of kotlin is we can use try and catch as an expression. It means it will return a value.

fun main() {
    val a: Int = 10
    val b: Int = 5
    val div: Int = try {
        a / b
    } catch (exception: ArithmeticException) {
        -1
    }
    println("The division of a and b is: $div")
}

Output ->
for input values 
a = 10  &  b = 5
The division of a and b is: 2
for input
a = 10  &  b = 0
The division of a and b is: -1

In the above program the result will be stored in div variable. If exception occurs it will return -1 to div variable.

4.3 Multiple catch blocks

A try block can have multiple catch blocks. Multiple catch blocks are used to catch different type of exceptions.

The below program is an example in which we will catch NumberFormatException and ArithmeticException.

fun main() {
    try {
        val a: Int = Integer.parseInt(readLine())
        val b: Int = Integer.parseInt(readLine())
        println("The Division of $a and $b is: ${a / b}")
    } catch (e: NumberFormatException) {
        println("Numbers entered are invalid!! and Caused exception $e")
    } catch (e: ArithmeticException) {
        println("Divided by zero!! and Caused exception $e")
    }
}

Output ->
for input values
a = 10  &  b = 2
The Division of 10 and 2 is: 5 and caused exception
a = 10  &  b = hello
Numbers entered are invalid!! and caused exception
for inputs
a = 10  &  b = 0
Divided by zero!! and caused exception

4.4 Nested try catch

If a try-catch block is placed inside another try-catch block this type of arrangement is called Nested try catch. The exception that occurred in inner try block is first checked and handled by inner catch block.

fun main() {
    try {
        try {
            val a: Int = Integer.parseInt(readLine())
            val b: Int = Integer.parseInt(readLine())
            println("Result of division is: ${a / b}")
        } catch (e: NumberFormatException) {
            println("Enter valid inputs. Caused exception $e")
        }
    } catch (e: ArithmeticException) {
        println("Division by zero is not allowed. Caused exception $e")
    }
}

Output ->
for input values
a = 10  &  b = 2
The Division of 10 and 2 is: 5
for input
a = 10  &  b = hello
Numbers entered are invalid. Caused exception.
for inputs
a = 10  &  b = 0
Divided by zero. Caused exception.

First we will take input from user and catch the exception with inner catch block if the format of the number is incorrect. Later we will perform division, and if any exception occurs during division, we will catch the exception with outer catch block.

4.5 finally block

The statements present in this block will always execute regardless of whether exception occurs in try block or not. The finally block can be placed after catch block or try block directly.

The below program is an example for finally block.

fun main() {
    try {
        val a: Int = 10
        val b: Int = 5
        println("Division of $a and $b results in: ${a / b}")
    } catch (exception: ArithmeticException) {
        println("Division by zero!!")
    } finally {
        println("Finally block is always executed.")
    }
}

Output ->
Division of 10 and 5 results in: 2
Finally block is always executed.

4.6 throw keyword

The Kotlin throw keyword is used to throw an exception explicitly. We can also create our own custom exception and throw it using the Kotlin throw keyword.

The syntax of the throw keyword is:
throw Exception(“Exception Message”)

Let us throw an exception when the age entered by user is less than 0:

fun main() {
    val age = -1
    if (age < 0) {
        throw IllegalArgumentException("Age must be greater than 0")
    } else {
        println("The age you entered is $age")
    }
}

Exception message ->
Exception in thread "main" java.lang.IllegalArgumentException: Age must be greater than 0
	at MainKt.main(main.kt:4)
	at MainKt.main(main.kt)

The above code will throw an exception because we passed negative value to age() function.

5. Project code and resources

That’s it! Thanks for reading the article, hope it was helpful !

Here We Go Again : (

if (article == helpful) {
    println("Like and subscribe to blog newsletter.")
} else {
    println("Let me know what i should blog on.")
}

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.