In this tutorial we will discuss about Kotlin exception handling concept in detail.
Errors, Exceptions are the developer’s nightmare as well as help guide. Let’s understand the basic difference between Error and Bugs.
Anything which breaks the normal flow of control of a program can be termed as an Exception.
Some of the most common examples of the exceptions are “Divide by ZERO”, “Index out of bound” amongst others. During cases as described below, the compiler encounters an Exception and breaks the flow of program. That makes it mandatory for us to handle exceptions.
Whenever a program encounters an exception, using Exception Handling we can handle errors and prevent the application or program from closing or terminating. In such cases, exceptions can be handled by showing user an alert and continuing the program flow.
Before we understand the use of exceptions in Kotlin, let’s have a look at broad classification of exception that were there in Java:
- Checked Exceptions
- Unchecked Exceptions
Exceptions which are basically checked at the time of compilation are termed as Checked Exceptions.
Example: FileNotFoundException.
Checked vs Unchecked Exceptions
Checked Exceptions are typically set on methods and are checked at compile time.
On the contrary, unchecked exceptions derive from RuntimeExceptions, these exceptions arise at runtime. For Instance, consider the NullPointerException.
Lets have a look at exceptions in Kotlin.
Kotlin Exception Handling
Kotlin Throwable Class
All the exception classes are inherited from the Throwable class. Let’s have a look at basic structure of Throwable class.
The declaration of the class Throwable, only the instances of this class can be thrown or caught.
open class Throwable
Properties of Throwable:
open val cause: Throwable?
cause
the cause of this throwable.
message
open val message: String?
the detail message string.
Constructors of the Throwable
The throwable class provides four constructors as described below:
<init>(message: String?)
message – String type having detailed message.
<init>(cause: Throwable?)
message – String type having detailed message.
<init>()
The default constructor with no parameter.
<init>(message: String?, cause: Throwable?)
message – String type having detailed message.
cause – the cause of this throwable.
To throw an exception we use the example below:
throw Exception("Hello Exception!")
The above code throws an Exception with the message as “Hello Exception”. The output of the code would be as below:
Exception in thread “main” java.lang.Exception: Hello Exception!
at FileKt.main (File.kt:2)
at FileKt.main (File.kt:-1)
Since we have seen how to throw an exception, let’s see how we should handle exceptions.
Try catch finally Block
The syntax of the try and catch would be as below:
try { // The code which could generate an exception, like “DivideByZero”, “NullPointer” and so-on. } catch (e: AnyException) { // Code handling goes here. Any sessions or connections which are no further longer needed place here. } finally { // The finally block which should be executed irrespective of excpetion being occurring or not. }
Like Java, Kotlin supports multiple catch blocks. Finally block is optional. We are free to use try with finally block also. Most important condition here is the if we use try block, we should use either one catch or a finally block.
Lets try to understand try, catch and finally block using an example:
fun main(args: Array<String>) { try { val str = "Hello World" str.toInt() } catch (exp : ArithmeticException) { println("The Program threw :ArithmeticException ") exp.printStackTrace() // to print the stack trace of the exception } catch (exp: Exception) { println("Exception Occured : Exception exp is"+exp) exp.printStackTrace() } finally { println("Program Termination Stopped.") } }
Output:
Exception Occured : Exception exp isjava.lang.NumberFormatException: For input string: “Hello World”
java.lang.NumberFormatException: For input string: “Hello World” at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Integer.parseInt(Integer.java:580) at java.lang.Integer.parseInt(Integer.java:615) at FileKt.main(File.kt:4) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.jetbrains.webdemo.executors.JavaExecutor.main(JavaExecutor.java:44) <outStream>Program Termination Stopped. </outStream>
Since, it was not possible to convert String to Integer the program terminated and printed the stack trace of the exception. Lets have a look at program where we don’t print the stack trace:
fun main(args: Array<String>) { try { val str = "Hello World" str.toInt() } catch (exp : ArithmeticException) { println("The Program threw :ArithmeticException ") exp.printStackTrace() } catch (exp: Exception) { println("Exception Occured : Exception.") } finally { println("Program Terminated") } }
Output:
Exception Occured : Exception exp isjava.lang.NumberFormatException: For input string: “Hello World”
The “try” Expression
“try” may have a return value also, hence is referred as an expression. Like every expression evaluation we can have try block to evaluate an expression and return the result or store the result to a variable. Refer the example below for further understanding:
fun main(args: Array<String>) { var str= "Hello World" var opStr = try { str.toInt() } catch (e: NumberFormatException) { "Cannot Convert String to an Integer." } // The variable output string is assigned a value in try block, in case when the value encountered is string, it sets the message which can be set in catch block. println(opStr) str = "5" opStr = try { str.toInt() } catch (e: NumberFormatException) { "Cannot Convert String to an Integer." } println(opStr) }
Output:
Cannot Convert String to an Integer.
5
In the first case, the value was String hence the compiler saved the output String as “Cannot Convert String to an Integer” and in the next case the compiler found that the value can be assigned and hence the value 5 is returned. In simple words, “if the try statement fails to execute the value of catch is set, else the value of try is set”. Interesting thing here is that finally does not returns a value.
Assertion in Kotlin: The ‘fail’ function
Using try with catch to return a value in Kotlin also has a shorthand when you wish to set a value in a particular catch block using the fail function.
Fail function Example:
import kotlin.test.fail class Person { var name: String? = "" } fun main(args: Array<String>) { var p = Person() p.name = null val n: String = p.name ?: fail("Name is empty or null.") print(5) }
Output:
Exception in thread “main” java.lang.AssertionError: Name is empty or null.
at kotlin.test.DefaultAsserter.fail (DefaultAsserter.kt:16)
at kotlin.test.AssertionsKt__AssertionsKt.fail (Assertions.kt:92)
at kotlin.test.AssertionsKt.fail (:1)
at FileKt.main (File.kt:13)
In this case, if fail function is called, the fail message being used is passed as Arguments and the program is terminated. In simple words, in case the fail function is called, the program is terminated and any code blocks post that are not executed.
Let’s understand the definition of fail function to understand see how it works:
fun fail(message: String): Nothing { throw IllegalArgumentException(message) }
The fail function, implicitly calls throws keyword.
In the example of fail function demonstrated above, the function prints the stack trace of the exception occurred which sometime we won’t always want to be displayed. In such cases, we can override the fail function to simply print the message.
So, we override the fail function like below:
import kotlin.test.fail class Person { var name: String? = "" } fun main(args: Array<String>) { var p = Person() p.name = null val n: String = p.name ?: fail("Name is empty or null.") print(5) } fun fail(message: String): Nothing { val throwable = Throwable(message) Thread.setDefaultUncaughtExceptionHandler { t, e -> System.err.println(e.message) } throw throwable }
Output:
Name is empty or null.
The output of the above code, will be only the message printed.
Exception Handling using Inline Functions
As seen above, the need for try and catch block is important and helps us to keep our application running. But due to multiple try and catch blocks, the code becomes cumbersome and difficult to debug. So, kotlin came with the support of inline functions for exception handling.
fun main(args: Array<String>) { inTryCatch { var a = 10 var b = 0 var c = a / b } inTryCatch { var str = "Hello World" var num = str.toInt() } } inline fun inTryCatch(action: () -> Unit) { try { action() } catch (t: Throwable) { println("Exception Caught : ${t.message}") } }
Here, in the example above, we define an inline function as “inTryCatch”, which accepts a block of code as parameter, and is executed inside the try block, if error occurs, the same is caught and the catch block is executed.
Output:
Exception Caught: / by zero
Exception Caught: For input string: “Hello World”
The aim of using inline functions is there ease of use and no overhead.
So, that was the basis of exception handling in Kotlin and we discussed about the how to handle exceptions to make sure that program is not terminated abruptly. Interesting thing to be noted is that Kotlin does not supports checked Exceptions, the reason for the same as mentioned in the official documentation is as below:
In case of smaller programs, requiring of exception specifications enhances both the code quality and developers productivity, but for large soft wares or enterprise level applications, the results are complete opposite, as these specification reduce developers productivity have little or no significant increase in code quality.