In this tutorial, we’ll look into Kotlin Null Safety. NullPointerException is one of the most common type of bugs any programmer witnesses in their projects. Let’s see how Kotlin deals with it.
Kotlin Null Safety
Kotlin compiler by default doesn’t allow any types to have a value of null
at compile-time.
For Kotlin, Nullability is a type. At a higher level, a Kotlin type fits in either of the two.
- Nullability Type
- Non-Nullability Type
Nullability as a Type
We can’t define the following syntax in Kotlin.
1 2 3 4 5 |
var str: String = "JournalDev Kotlin Archives" str= null //compilation error var a : String = null // compilation error. |
To set a type as Nullable we need to append a ?
to the type as shown below.
1 2 3 4 5 |
var a : String? = null var newStr : String? = "Kotlin Null Safety" newStr = null |
Now whenever we access a Nullable Referernce, we need to handle the null cases carefully.
Note: Adding a ? changes the type of a variable from String to String?, Int to Int? and so on.
The most common way is using the if else statements as seen below.
1 2 3 4 5 6 7 8 9 |
if(a!=null) { print("The value of a is $a") } else{ print("Sorry a is null") } |
Now the above approach can lead to plenty of if else nested brackets. Kotlin does provide a simpler way to access nullable references using Safe Calls.
Safe Calls
To call a function or a property over a variable, we typically do the following thing.
1 2 3 4 |
var a : String = "Hello" print(a.length) |
This WON’T always work with Nullable References.
1 2 3 4 5 6 |
var a : String? = "Hi" print(a.length) //prints 2 a = null print(a.length) // compilation error |
The dot reference can’t be used when the Nullable reference holds a null value.
Hence, Kotlin provides us with a safe call operator to use over Nullable References as shown below.
1 2 3 4 5 6 |
a = "Hi" print(a?.length) //prints 2 a = null print(a?.length) //prints null |
The safe call ?.
executes the relevant call only when the value is non-null.
Else it prints a null for you.
Safe calls are handy when you need to keep nullability checkers over multiple variables as shown below.
1 2 3 |
var streetName : String? = address?.locality?.street?.addressLine1 |
There’s another way to call properties on a nullable reference.
!! Operator
Contrary to the safe calls, the !!, also known as the not-null assertion, converts the nullable reference into its non-nullable type irrespective of whether it’s a null or not. This should be used only when you’re certain that the value is NOT NULL. Else it’ll lead to an NPE exception.
1 2 3 4 5 6 |
a = null println(a!!.length) // runtime error. a = "Hi" print(a!!.length) //prints 2 |
Safe Casting
The ? operator can be used for preventing ClassCastException
as well(another commonly seen exception).
1 2 3 4 5 |
var b : String ="2" var x : Int? = b as? Int println(x) //prints null |
In the above code, we’re safe cast the variable b using
1 |
as? |
. If the cast isn’t successful, the value is set to null thereby preventing ClassCastException
.
Another example:
1 2 3 4 5 6 7 |
var array1 : Array<Any?> = arrayOf("1","2","3") var i : Int? = array1[0] as? Int println(i) //prints null var s : String? = array1[0] as? String print(s) //prints 1 |
Elvis Operator
Up until now, whenever the safe call operator returns a null value, we don’t perform an action and print null instead. The Elvis operator ?:
allows us to set a default value instead of the null as shown below.
1 2 3 4 5 6 |
var newString : String? = "JournalDev.com" println(newString?.length) //prints 14 newString = null println(newString?.length?:"-1") //prints -1 |
The elvis operator is equivalent to the following:
1 2 3 4 5 6 7 8 9 |
if(newString!null) { print(newString.length) } else{ print("-1") } |
Another sample:
1 2 3 |
var streetName : String? = address?.locality?.street?.addressLine1 ?: "Street Name not found" |
Using let()
Let function executes the lamda function specified only when the reference is non-nullable as shown below.
1 2 3 4 5 6 |
newString = " Kotlin from Android" newString?.let { println("The string value is: $it") } newString = null newString?.let { println("The string value is: $it") } |
The statement inside let is a lamda expression. It’s only run when the value of newString is not null.
it
contains the non-null value of newString
Using also()
also
behaves the same way as let
except that it’s generally used to log the values. It can’t assign the value of it
to another variable.
Here’s an example of let and also changed together.
1 2 3 4 5 |
var c = "Hello" newString = " Kotlin from Android" newString?.let { c = it }.also { println("Logging the value: $it") } |
Note: The statement present inside let
can’t be placed in also
. Vice-versa can work though.
Filtering Out Null Values
We can filter out Null Values from a collection type using the following function:
1 2 3 4 5 |
var array2: Array<Any?> = arrayOf("1", "2", "3", null) var newArray = array2.filterNotNull() println(newArray.size) //prints 3 |
Java Interoperability
Since Java doesn’t force you to state type as Nullable or Non-Nullable type, Kotlin compiler doesn’t give an error for the following:
1 2 3 |
var javaObject = MyClass(null) |
If you set the Java Annotation @Nullable or @NotNull, the Kotlin compiler would consider them as Nullable or Not Nullable References.
Remember : Nullable references require a ? in Kotlin.
Project Structure
The Kotlin Project below contains various sub-topics we’ve covered in Kotlin till now.
That’s all we’ve got in Kotlin Null Safety. You can download the IntelliJ project that contains the various code snippets we’ve covered in Kotlin so far. Play around with it!
References : Official Docs