In Kotlin, a core principle is null safety, a feature designed to prevent the infamous NullPointerException (NPE), prevalent in Java and other languages, frequently causing frustrating bugs and application crashes.
To achieve this robust null safety, Kotlin's type system makes a crucial distinction:
- Non-nullable types: These are types that cannot hold a `null` value.
- Nullable types: These are types that can hold a `null` value.
Kotlin Non-nullable Types
In Kotlin, variables and function return types are non-nullable by default. This means that unless explicitly indicated otherwise, the compiler guarantees they will not be `null`.
Here variable declaraytion for `String` is non-nullable.
val nonNullableString: String = "This cannot be null" // Non-nullable String
To define a function with a **non-nullable** return type, declare the return type without the `?`. This function is guaranteed to return a non-null value of the specified type.
fun nonNullableMethod(): String {
// Function logic that always returns a non-null String
return "A non-null result"
}
Kotlin Nullable Types
To allow a variable or return type to accept `null` values, explicitly declare it as a nullable type by adding a question mark (?) to the end of the type declaration.
Here varibale decalrtion `String?` is nullable.
val nullableString: String? = null // Nullable String
To define a function with a nullable return type, add ? to the type declaration. Such functions can return either a value of the specified type or null.
fun nullableMethod(): String? {
// Function logic that can return null or a non-null value
return null
}
Null-Safety handling in Kotlin
Kotlin provides powerful built-in tools to handle nullable variables safely and avoid runtime exceptions. Here are the main approaches:
-
Safe Calls (?.): Use the
?.operator to safely access properties or methods on nullable objects. Returnsnullif the object is null.val length: Int? = nullableString?.length // Returns null if nullableString is null
-
Elvis Operator (?:): Provides a default value when a nullable expression is null.
val result: String = nullableString ?: "Default Value" // Uses "Default Value" if nullableString is null
-
Explicit Null Checks: Traditional null checks with
ifconditions for more control.if (nullableString != null) { val length = nullableString.length // Smart cast to non-null } else { // Handle null case } -
checkNoNull(): A custom function to enforce non-null values.
fun checkNoNull(value: Any?) { require(value != null) { "Value cannot be null" } } // Usage: checkNoNull(potentialNullValue) // Throws IllegalArgumentException if null -
Not-null Assertion Operator (!!): Converts any value to a non-null type and throws
NullPointerExceptionif the value is null.val nonNullValue: Int = nullableString!!.length // Throws NPE if nullableString is null
Important:
- Prioritize the use of safe calls (
?.) and the Elvis operator (?:) whenever applicable. - Employ explicit null checks (e.g., using `if` statements) for situations requiring more intricate null handling logic or when other options aren't suitable.
- Avoid the not-null assertion operator (
!!) whenever possible. Overuse of `!!` undermines Kotlin's null safety and can reintroduce the risk of `NullPointerException`s. - When absolutely confident that a value is not null but the compiler can't guarantee it, prefer Kotlin's built-in functions like
checkNotNull()orrequireNotNull()instead of `!!`. - If there is need of *must* use of the `!!` operator, document reasoning clearly with a comment explaining why using it and why certain the value won't be null.