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
name
isnull
,length
will benull
(since the safe call returnsnull
fornull
receivers). - If
name
is notnull
,length
will 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
expression1
is notnull
, thenexpression1
is returned. - If
expression1
isnull
, thenexpression2
is returned.
Example1 of Elvis Operator:
val nullableName: String? = null
val nonNullName: String = nullableName ?: "Unknown"
println(nonNullName) // Prints "Unknown"
In this example:
nullableName
isnull
.nonNullName
is assigned the result ofnullableName ?: "Unknown"
.- Since
nullableName
isnull
,"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).userName
is 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?.name
accesses thename
property ofperson
, which isnull
in this case.personName
is assigned"Anonymous"
becauseperson?.name
evaluates 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:
nullableString
is a nullableString?
initialized with a non-null value “Hello”.- We use
!!
to assert thatnullableString
is not null, sonullableString!!.length
retrieves the length of the string “Hello” (which is 5). - If
nullableString
were null (likeanotherNullableString
), using!!
on it (anotherNullableString!!
) would throw aNullPointerException
because 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.