Not-Null Assertions in Kotlin

Categories:
6 minute read
Not-Null Assertions in Kotlin: Proceed with Caution
Kotlin’s null safety system is a significant advancement in preventing NullPointerExceptions
. It encourages developers to explicitly declare whether a variable can hold a null value, leading to more robust and predictable code. While Kotlin’s type system excels at enforcing null safety at compile time, there are situations where you, as the developer, have more knowledge about the runtime state than the compiler. In these specific cases, you might be certain that a nullable variable will not be null at a particular point in your code. This is where the not-null assertion operator (!!
) comes in. However, it’s a tool that should be used with extreme caution, as misuse can reintroduce the very null pointer exceptions that Kotlin’s type system is designed to prevent.
Understanding Nullability in Kotlin
Before diving into not-null assertions, it’s crucial to understand Kotlin’s approach to nullability. Kotlin distinguishes between two types:
- Nullable types: These are explicitly declared to allow null values. You indicate a nullable type by appending a question mark (
?
) to the type declaration. For example,String?
represents a string that can be either a valid string ornull
. - Non-nullable types: These are guaranteed to never hold a null value. They are the default in Kotlin. For example,
String
represents a string that is guaranteed to be a valid string.
This distinction allows the compiler to perform static analysis and catch potential null pointer exceptions at compile time.
Introducing the Not-Null Assertion Operator (!!)
The not-null assertion operator (!!
), sometimes called the “bang-bang” operator, is a way to tell the Kotlin compiler that you are absolutely certain that a nullable variable is not null at a specific point in the code. It’s a way to override the compiler’s null safety checks.
The syntax is simple: you append !!
to the nullable variable. For example:
val name: String? = getNameFromSomewhere() // Could return null
val nameLength: Int = name!!.length
In this example, the !!
tells the compiler, “I know that name
is not null here, so treat it as a non-nullable String
.” If name
is actually null at runtime, a NullPointerException
will be thrown.
Why Use Not-Null Assertions?
The primary reason to use not-null assertions is when you have information about the runtime state that the compiler cannot infer. This often occurs in situations involving:
- Interoperability with Java: Java code doesn’t have the same null safety guarantees as Kotlin. When interacting with Java code, you might receive nullable values that you know will never be null in your specific use case.
- Late initialization: Sometimes, you might initialize a variable in a later part of your code, such as in an
onCreate()
method in Android development. The compiler might not be able to infer that the variable is initialized before it’s used. - Specific control flow: You might have logic that guarantees a nullable variable is not null at a particular point, but the compiler cannot follow that logic.
Examples of Not-Null Assertions
Here are some examples illustrating the use of not-null assertions:
// Example 1: Interoperability with Java
val javaString: String? = someJavaMethod() // Java method might return null
val kotlinString: String = javaString!! // We are sure the Java method won't return null in this specific case
// Example 2: Late Initialization
lateinit var myString: String
fun initializeString() {
myString = "Initialized!"
}
fun useString() {
initializeString()
println(myString.length) // myString is guaranteed to be initialized here. No need for !!
}
// Example 3: Specific Control Flow
fun processString(input: String?) {
if (input != null) {
val length: Int = input.length // Inside this if block, input is guaranteed to be non-null
println("Length: $length")
} else {
// ... handle null case ...
}
}
The Dangers of Not-Null Assertions
The not-null assertion operator is a double-edged sword. While it can be useful in certain situations, it also has significant drawbacks:
- Reintroducing NullPointerExceptions: If you are wrong about the variable not being null, the
!!
operator will throw aNullPointerException
at runtime, defeating the purpose of Kotlin’s null safety system. - Hiding potential bugs: Overuse of not-null assertions can mask underlying issues in your code. Instead of addressing the possibility of a null value, you are simply bypassing the null safety checks.
- Making code less predictable: Excessive use of
!!
can make it harder to reason about your code and understand where null values might occur.
Alternatives to Not-Null Assertions
In most cases, there are better alternatives to using not-null assertions. These include:
- Safe calls (
?.
): Use safe calls to access properties and methods of nullable objects without fear of exceptions. - Elvis operator (
?:
): Use the Elvis operator to provide default values or execute alternative logic when a value is null. let
block: Use thelet
block to perform operations on a non-null object only if it’s not null.- Null checks (
if
): Use traditionalif
statements to check for null values and handle them appropriately. - Refactoring: Often, the best solution is to refactor your code to avoid the need for not-null assertions altogether. This might involve redesigning how you handle null values or restructuring your code.
Best Practices for Using Not-Null Assertions
If you absolutely must use a not-null assertion, follow these best practices:
- Use it sparingly: Only use
!!
when you are absolutely certain that the value is not null and there is no better alternative. - Document why you are using it: Add a comment explaining why you are using the not-null assertion and why you are confident that the value is not null. This will help other developers (and your future self) understand your code.
- Consider alternatives first: Always explore other options, such as safe calls, the Elvis operator, or refactoring, before resorting to not-null assertions.
- Test thoroughly: If you use
!!
, make sure to test your code thoroughly to ensure that you are not introducing potential null pointer exceptions.
Conclusion
The not-null assertion operator (!!
) is a powerful but dangerous tool in Kotlin’s null safety system. While it can be useful in specific situations, it should be used with extreme caution. Overuse or misuse can undermine the benefits of Kotlin’s null safety and reintroduce the very errors it is designed to prevent. In most cases, there are better alternatives, such as safe calls, the Elvis operator, or refactoring. Always consider these alternatives before resorting to not-null assertions, and if you do use !!
, document your reasoning clearly and test your code thoroughly. Remember, the goal is to write safe, predictable, and maintainable code, and over-reliance on not-null assertions can hinder that goal.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.