Kotlin’s type system is aimed to eliminate the jeopardy of null reference from the code because it is a billion dollar mistake. NullPointerExceptions are thrown by the program at runtime and sometimes cause application failure or system crashes.
If anybody has been programming in Java or other language that has concept of null reference then he must has experienced about NullPointerException in the code. Kotlin compiler also throws NullPointerException if it found any null reference without executing any other statements.
The possible causes of NullPointerException’s are following:
- Explicit call to throw NullPointerException()
- Use of the !! operator
- Some data inconsistency with regard to initialization e.g. an uninitialized this is passed as an argument.
- Java interoperations such as attempts to access a member on a null reference , generics type with incorrect nullability.
Nullable and Non-Nullable Types in Kotlin –
Kotlin type system has distinguish two types of references that can hold null (nullable references) and those that can not (non-null references).
A variable of type String can not hold null. If we try to assign null to the variable, it gives compiler error.
var s1: String = "Geeks" s1 = null // compilation error
To allow a variable to hold null, we can declare a variable as nullable string, written String?
var s2: String? = "GeeksforGeeks" s2 = null // ok print(s2)
Now, if we want to access the length of the string s1, it guarantees not to throw NPE, so we can safely say:
val l = s1.length
But if we want to access the length of the string s2, that would not be safe, and the compiler reports an error:
val l = s2.length // error: variable 's2' can be null
Kotlin program of non-nullable type –
fun main(args: Array<String>){ // variable is declared as non-nullable
var s1 : String = "Geeks"
//s1 = null // gives compiler error
print( "The length of string s1 is: " +s1.length)
} |
Output:
The length of string s1 is: 5
Here, if we try to assign null to a non-nullable variable then it gives compiler time error. But, if we try to access the length of the string then it guarantees not to throw NullPointerException.
Kotlin program of nullable type-
fun main(args: Array<String>) { // variable is declared as nullable
var s2: String? = "GeeksforGeeks"
s2 = null // no compiler error
println(s2.length) // compiler error because string can be null
} |
Output:
Error:(8, 15) Kotlin: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
Here, we can easily assign null to a nullable type variable. But we should use the safe operator to get the length of the string.
Checking for null in conditions –
The most common way of checking null reference is using if-else expression. We can explicitly check if variable is null, and handle the two options separately.
Kotlin program of checking null in conditions –
fun main(args: Array<String>) { // variable declared as nullable
var s: String? = "GeeksforGeeks"
println(s)
if (s != null ) {
println( "String of length ${s.length}" )
} else {
println( "Null string" )
}
// assign null
s = null
println(s)
if (s != null ) {
println( "String of length ${s.length}" )
} else {
println( "Null String" )
}
} |
Output:
GeeksforGeeks String of length 13 null Null String
Note that we have used if-else block to check the nullability. If string contains null then it executes the if block else it executes the else block.
Safe Call operator(?.) –
Null Comparisons are simple but number of nested if-else expression could be burdensome. So, Kotlin has a Safe call operator, ?. that reduces this complexity and execute an action only when the specific reference holds a non-null value.. It allows us to combine a null-check and a method call in a single expression.
The following expression:
firstName?.toUpperCase()
is equivalent to:
if(firstName != null) firstName.toUpperCase() else null
Kotlin program of using safe operator –
fun main(args: Array<String>) { // variable declared as nullable
var firstName: String? = "Praveen"
var lastName: String? = null
println(firstName?.toUpperCase())
println(firstName?.length)
println(lastName?.toUpperCase())
} |
Output:
PRAVEEN 7 null
We can use the safe call operator with let(), also() and run() if value is not null-
let() method –
To execute an action only when a reference holds a non-nullable value, we can use a let operator. The lambda expression present inside the let is executed only if the variable firstName is not null.
val firstName: String? = null firstName?.let { println(it.toUpperCase()) }
Here, the variable firstName is null, so the lambda expression is not executed to convert the string to Upper Case letters.
Kotlin program of using let –
fun main(args: Array<String>) { // created a list contains names
var stringlist: List<String?> = listOf( "Geeks" , "for" , null , "Geeks" )
// created new list
var newlist = listOf<String?>()
for (item in stringlist) {
// executes only for non-nullable values
item?.let { newlist = newlist.plus(it) }
}
// to print the elements stored in newlist
for (items in newlist){
println(items)
}
} |
Output:
Geeks for Geeks
also() method chain with let() –
If we want to apply some additional operation like printing the non-nullable items of the list we can use an also() method and chain it with a let() or run():
fun main(args: Array<String>) { // created a list contains names
var stringlist: List<String?> = listOf( "Geeks" , "for" , null , "Geeks" )
// created new list
var newlist = listOf<String?>()
for (item in stringlist) {
// executes only for non-nullable values
item?.let { newlist = newlist.plus(it) }
item?.also{it -> println(it)}
}
} |
Output:
Geeks for Geeks
run() method –
Kotlin has a run() method to execute some operation on a nullable reference. It seems to be very similar to let() but inside of a function body, the run() method operates only when we use this reference instead of a function parameter:
fun main(args: Array<String>) { // created a list contains names
var stringlist: List<String?> = listOf( "Geeks" , "for" , null , "Geeks" )
// created new list
var newlist = listOf<String?>()
for (item in stringlist) {
// executes only for non-nullable values
item?.run { newlist = newlist.plus( this ) } // this reference
item?.also{it -> println(it)}
}
} |
Output:
Geeks for Geeks
Elvis Operator(?:) –
The Elvis operator is used to return a non-null value or a default value when the original variable is null. In other words, if left expression is not null then elvis operator returns it, otherwise it returns the right expression. The right-hand side expression is evaluated only if the left-hand side found to be null.
The following expression:
val name = firstName ?: "Unknown"
is equivalent to:
val name = if(firstName!= null) firstName else "Unknown"
Moreover, we can also use throw and return expressions on the right side of Elvis operator and it is very useful in functions. Hence, we can throw an exception instead of returning a default value in the right side of Elvis operator.
val name = firstName ?: throw IllegalArgumentException("Enter valid name")
Kotlin program of using Elvis operator –
fun main(args: Array<String>) { var str : String? = "GeeksforGeeks"
println(str?.length)
str = null
println(str?.length ?: "-1" )
} |
Output:
13 -1
Not null assertion : !! Operator
The not null assertion (!!) operator converts any value to a non-null type and throws an exception if the value is null.
If anyone want NullPointerException then he can ask explicitly using this operator.
fun main(args: Array<String>) { var str : String? = "GeeksforGeeks"
println(str!!.length)
str = null
str!!.length
} |
Output:
13 Exception in thread "main" kotlin.KotlinNullPointerException at FirstappKt.main(firstapp.kt:8)