Null safety is a programming concept that aims to prevent unexpected NullPointerExceptions in your code. In Kotlin, null safety is enforced by the type system, which means you must explicitly specify whether a variable or property can hold a null value (nullable) or cannot hold a null value (non-nullable).
Null Safety Prevents Runtime Crashes: Null safety helps catch potential NullPointerExceptions at compile time rather than runtime, reducing the risk of unexpected crashes in your application.
Nullable Type(?)
In Kotlin, every type is non-nullable by default. This means that a variable of type cannot hold a null value unless you explicitly declare it as nullable by appending ? to the type (String?). This distinction helps catch potential null pointer exceptions at compile time.
var regularString: String = "Hello" // Non-nullable String
If you try to assign value to the non-nullable variable, it gives compile time errors. Null can not be a value of a non-null type String
regularString = null // compilation error
To create nullable type you must specify whether a variable can hold null by appending a ? after the type declaration. For example:
var name: String? = "John"
name = null // This is valid because name is nullable
Here, name is declared as a nullable String (String?). This means name can hold either a valid String value or a null value.
Non-nullable Type
A non-nullable type is a type that cannot hold a null value. When you declare a variable with a non-nullable type, Kotlin ensures that the variable always holds a non-null value throughout its lifecycle, thereby preventing NullPointerExceptions at runtime. To declare a variable with a non-nullable type, you simply specify the type without the nullable (?) modifier.
val nonNullableString: String = "Hello"
val nonNullableInt: Int = 42
Kotlin’s non-nullable types provide compile-time safety against NullPointerExceptions. If you attempt to assign null to a variable with a non-nullable type, the compiler will raise an error.
By using non-nullable types, you can catch nullability issues early in the development process, reducing the likelihood of encountering unexpected runtime errors related to null values.
Safe Calls (?.) operator
Kotlin provides the safe call operator (?.), which allows you to safely access properties or call methods on nullable objects. If the object is null, the call will return null instead of throwing a null pointer exception.
It prevents a NullPointerException from occurring if the object reference is null by simply returning null instead of trying to access the property or method.
val name: String? = null
val length: Int? = name?.length
print(length) // null
Here in this example, name is declared as a nullable String initialized to null. Here name?.length Uses the safe call operator (?.) to access the length property of name.
- If
nameisnull,lengthwill benull(since the safe call returnsnullfornullreceivers). - If
nameis notnull,lengthwill be the length of theString.
Safe calls simplify code by eliminating the need for explicit null checks before accessing properties or invoking methods. This leads to cleaner and more concise code, focusing on the logic rather than handling nullability at every step.
val name:String? = null
var length = if (name != null) name.length else null
println(length)//null
// With safe call
length = name?.length
println(length)//null
Safe calls can be chained (?.) to perform a sequence of operations on a chain of nullable properties or methods. If any part of the chain is null, the entire expression will evaluate to null without throwing an exception.
val result = user?.address?.city?.toUpperCase()
// 'result' will be null if any intermediate property or method call returns null
Elvis Operator (?:)
The Elvis operator (?:) in Kotlin is used to assign a default value when dealing with nullable references. It helps handle situations where you have a nullable variable or property, allowing you to provide a fallback value if the expression on the left side is null. This makes managing nullability more concise and straightforward.
It’s commonly used to avoid explicit null checks and provide fallback values when dealing with nullable variables.
The Elvis operator ?: is a concise way to handle nullable expressions and provide default values.
The Elvis operator has the following syntax:
expression1 ?: expression2
- If
expression1is notnull, thenexpression1is returned. - If
expression1isnull, thenexpression2is returned.
Example1 of Elvis Operator:
val nullableName: String? = null
val nonNullName: String = nullableName ?: "Unknown"
println(nonNullName) // Prints "Unknown"
In this example:
nullableNameisnull.nonNullNameis assigned the result ofnullableName ?: "Unknown".- Since
nullableNameisnull,"Unknown"is assigned tononNullName.
Example2 : Using with Method Call:
fun getUserName(): String? {
// Simulate getting username from a remote source
return null
}
val userName: String = getUserName() ?: "Guest"
println("Welcome, ${userName.capitalize()}") // Prints "Welcome, Guest"
Here:
getUserName()returnsnull(simulating failure to retrieve a username).userNameis assigned the result ofgetUserName() ?: "Guest".- Since
getUserName()isnull,"Guest"is assigned touserName.
Example3: Accessing Property of Nullable Object:
data class Person(val name: String?)
val person: Person? = Person(null)
val personName: String = person?.name ?: "Anonymous"
println("Person's name: $personName") // Prints "Anonymous"
Explanation:
person?.nameaccesses thenameproperty ofperson, which isnullin this case.personNameis assigned"Anonymous"becauseperson?.nameevaluates tonull.
Not null assertion (!!) Operator :
The not-null assertion operator (!!) in Kotlin is used to convert any value to a non-null type and throw a NullPointerException if the value is actually null. When you use !! on a nullable reference, you’re telling the compiler that you are sure the value is not null, and if it is, a NullPointerException will be thrown at runtime.
Here’s an example to illustrate how the !! operator works:
fun main() {
val nullableString: String? = "Hello"
val length = nullableString!!.length
println("Length of the string: $length") // Output: Length of the string: 5
val anotherNullableString: String? = null
// Uncomment the line below to see the NullPointerException
// val length2 = anotherNullableString!!.length // This will throw NullPointerException
}
In the example above:
nullableStringis a nullableString?initialized with a non-null value “Hello”.- We use
!!to assert thatnullableStringis not null, sonullableString!!.lengthretrieves the length of the string “Hello” (which is 5). - If
nullableStringwere null (likeanotherNullableString), using!!on it (anotherNullableString!!) would throw aNullPointerExceptionbecause we are asserting that it’s not null when it actually is.
It’s important to be cautious when using the !! operator because if used incorrectly (i.e., on a null value), it will lead to runtime exceptions. It’s generally safer and recommended to use safer alternatives like safe calls (?.) or the Elvis operator (?:) to handle nullable values and avoid potential null pointer exceptions.