Kotlin, known for its modern features and null safety, often puzzles beginners with its approach to handling null values. One dilemma they frequently encounter is trying to use the !! operator on nullable variables. In this article, we will explore how Kotlin deals with nullability, why the !! operator might not be your best friend, and practical ways to handle nulls safely.
Understanding Nullability in Kotlin
Kotlin differentiates between nullable and non-nullable types, guarding against null pointer exceptions (NPE), a common source of frustration in many languages such as Java. In Kotlin, if a variable is declared as a non-null type, it cannot hold a null value. However, if it is declared as a nullable type — defined with a question mark (?) — it can hold null.
var nonNullable: String = "Hello, World!"
var nullable: String? = null
In the snippet above, nonNullable cannot be assigned null, whereas nullable can.
The Danger of the !! Operator
The !! operator is sometimes called the "not-null assertion operator". It promises the compiler that the variable is not null and can be treated as a non-nullable type. If the variable is null, using !! will throw a NullPointerException.
fun lengthOfText(text: String?): Int {
return text!!.length // Throws NullPointerException if text is null
}
// Usage
val length = lengthOfText(null) // Throws an error
As evident, while !! can sometimes be useful, it should be used cautiously. It can negate the safety benefits that Kotlin provides.
Safer Alternatives to !!
Thankfully, Kotlin offers several safer alternatives to handle nullable values without risking NPEs.
1. Safe Call Operator (?.)
This operator allows you to access a property or call a function on a nullable object. If the object is null, it will return null instead of throwing an exception.
fun lengthOfText(text: String?): Int? {
return text?.length
}
// Usage
val length = lengthOfText(null) // Returns null
val safeLength = lengthOfText("Kotlin") // Returns the length of "Kotlin"
Here, ?.length safely checks if text is null before attempting to access its length.
2. Elvis Operator (?:)
Named after the resemblance to a sideways emoticon for Elvis Presley’s style, this operator allows you to specify a default value when a nullable variable is indeed null.
fun lengthOfTextWithDefault(text: String?): Int {
return text?.length ?: 0
}
// Usage
val defaultLength = lengthOfTextWithDefault(null) // Returns 0
val lengthWithDefault = lengthOfTextWithDefault("Kotlin") // Returns length of "Kotlin"
In this example, ?: 0 returns 0 when text is null.
3. Safe Cast Operator (as?)
Kotlin provides a safe cast operator that will attempt to perform a cast if possible or, if not, return null instead of throwing an exception.
val anyValue: Any = "Kotlin"
val text: String? = anyValue as? String // Safe cast
// Safe Cast with Null
val anotherValue: Any = 1234
val anotherText: String? = anotherValue as? String // Returns null
As shown, you can avoid runtime errors by using as? which gracefully returns null in case of a failed cast.
Conclusion
While the !! operator may seem like an easy fix, its usage can introduce errors if not carefully managed. Employing Kotlin's built-in safety mechanisms like the safe call operator, Elvis operator, and safe cast operator can help you effectively manage nullability without sacrificing the reliability of your code.