Kotlin Safe Call Operator
The safe call operator ?. lets you access properties or call methods on a nullable variable without crashing. If the variable is null, the expression returns null instead of throwing an error.
The Problem Without Safe Call
var name: String? = null
println(name.length) // ERROR at compile time — cannot access on nullableUsing the Safe Call Operator
var name: String? = null
println(name?.length) // null — no crash
name = "Kotlin"
println(name?.length) // 6How ?. Works
name?.length
Is name null?
│
┌──────┴──────┐
YES NO
│ │
return null access .length
return the value
Chaining Safe Calls
data class Address(val city: String?)
data class User(val name: String, val address: Address?)
val user: User? = User("Ana", Address(null))
// Each ?. protects the next step
println(user?.address?.city) // null (city is null)
println(user?.name) // AnaSafe Call with Function
val text: String? = " hello world "
println(text?.trim()) // "hello world"
println(text?.uppercase()) // " HELLO WORLD "
println(text?.split(" ")?.size) // 3
val nullText: String? = null
println(nullText?.trim()) // null (no crash)Safe Call in Assignment
val cityName: String? = user?.address?.city
if (cityName != null) {
println("City: $cityName")
} else {
println("City not available")
}Safe Call with let
Combine ?. with let to run a block only when the value is not null:
val email: String? = "user@example.com"
email?.let {
println("Sending email to: $it")
println("Domain: ${it.substringAfter("@")}")
}
val noEmail: String? = null
noEmail?.let {
println("This block never runs")
}
// Nothing printed for noEmailPractical Example: Order Tracking
data class TrackingInfo(val status: String, val location: String?)
data class Order(val id: String, val tracking: TrackingInfo?)
fun main() {
val order1 = Order("ORD-001", TrackingInfo("In Transit", "Mumbai Depot"))
val order2 = Order("ORD-002", TrackingInfo("Processing", null))
val order3 = Order("ORD-003", null)
val orders = listOf(order1, order2, order3)
for (order in orders) {
val status = order.tracking?.status ?: "No update"
val location = order.tracking?.location ?: "Location unknown"
println("${order.id}: $status @ $location")
}
}Output:
ORD-001: In Transit @ Mumbai Depot
ORD-002: Processing @ Location unknown
ORD-003: No update @ Location unknown