This made me scratch my head a few times before understanding what those double-exclamation points mean in some Kotlin code references, but it seems it’s not that difficult to understand. This is another post in the “Fun with Kotlin” series. Btw, I didn’t explain before because I thought it’s easy to spot, I chose “Fun with Kotlin” because the word fun in Kotlin is what you start the declaration of a function with and it also means having a great time. I know, a bad dad joke but I couldn’t help myself.
Kotlin has a sort of safety feature compared to other programming languages called Null-Safety. Basically, in programming it can be a big problem if one of the references you make is to a null value. Referencing a null value in a programming language is also called “The Billion Dollar Mistake” and as mentioned it can be basically very serious.
What does that mean? null basically means there’s nothing there. It’s probably as big of a mistake as dividing something by 0. In kindergarden terms, the null value is basically the invisible man. And you cannot reference something that doesn’t exist. Well actually you can by calling it bogeyman, and in programming that’s the null reference.
This isn’t related only in Kotlin, it’s actually a pitfall in many programming languages, including Kotlin’s cousin Java. The problem is that when you access/use to a null reference, you will get a null reference exception. In Kotlin this would be a NullPointerException, or an NPE for short.
Kotlin handles this a bit more gracefully. It is capable to understand which reference can be null and differentiate those from the ones that can’t be null. Here’s an example, by default the regular String value cannot hold null values. So if you’re asking for input from users and they don’t enter anything, if you don’t treat that case you’d get an error:
fun main() {
var yourName: String //we're telling it that yourName is a string, but not that it can be null
yourName = null
println(yourName) //when it gets here, it discovers the value null and returns an error
}
This will return the error “Null can not be a value of a non-null type String“. That’s because Kotlin expects the variable yourName to be a valid string, not nothing. Furthermore, assuming your code gets past this and you reference a property of the yourName value (that can’t be null), it’ll work. Say for instance assigning the length property of yourName to a value nameLength, won’t produce an error if the initial value cannot be null:
fun main() {
var yourName: String //we're telling it that yourName is a string, but not that it can be null
val nameLength = yourName.length //this doesn't produce an error because yourName cannot be null
}
In the example above, you can easily allow null for the variable yourName by adding a simple question mark (?). So the example above becomes this:
fun main() {
var yourName: String?
yourName = null
println(yourName)
}
What this extra question mark added after the data type mean is “this variable can have any value, including null”. However, be careful when trying to access properties of elements that can be null. Because the variable yourName above can be null, the following reference will thrown an exception:
val nameLength = yourName.length //this produce an error: variable 'yourName' can be null
Checking for null reference in conditions
There are situations when you want your code to throw an error, especially during debugging. That’s where those double-exclamation points are used, the so-called double-bang (!!) operators. Basically when you use these, you tell it to throw an error if the reference is null. Let’s take the example above again:
fun main() {
var yourName: String?
yourName = null
println(yourName!!)
}
When you try to compile this code you’ll get an KotlinNullPointerException error. This happens because of the double-bang characters added after the name of the variable: yourName!! – in this case the compilers arrives here and says wait, this value is null and you told me to alert you when that happens and it did so do something!!
So we’ve seen there are ways to allow null as a value for variables and references. But how do you check if there’s a null reference in your condition? Well, by comparing that value with null. Here’s an example:
fun main() {
var yourName: String? // we're saying here that the value of yourName can be null
yourName = readLine() // getting input from the user
if (yourName != null) {
// do whatever you want here
}
else {
println("Please enter your name") //we're asking for the name again
yourName = readLine()
}
println(yourName)
}
This can get lengthy if you’re verifying if a variable is null each time (not in the example above because that requires a prompt, but in other situations). That’s why there are safe calls operators, such as ?.
Here’s an example to showcase what a safe call operator does:
val yourFirstName: String = "Elon"
val yourLastName: String? = null
println(yourLastName?.length) //this is a safe call and it evaluates to null
println(yourFirstName?.length) // Unnecessary safe call, as yourFirstName already has a value
What the safe call operator does above (?.) is basically this:
- checks to see if the variable yourLastName is null and returns the length of the string if it isn’t null. The type of this expression is Int?.
- if the variable is null, then the result will be null but without an error
Safe call checks are useful especially when using chained references. Say for instance you want to see the email for a user that’s in a particular group and you reference it like this:
user.group.email
You’ll receive an error each time one of those elements is null (i.e. the user doesn’t exist, or it’s not part of the group). To solve that, you add safe call checks on each reference like this:
user?.group?.email
What this does is it checks if the user exists, then if it’s part of that group and if any of these is null then it returns the value null.
Anyway, there’s a longer version of this and better structured in the Kotlin documentation: Null-safety in Kotlin
But what you need to understand is this:
- always take into account if a value you use can be null or not
- use “?” when you want to allow references to have the value null
- use double-bang (!!) after a function/parameter to return an error if that value is null