Kotlin let with Null
The let scope function runs a code block and passes the calling object as a parameter named it. Combined with the safe call operator ?., it creates a clean pattern for executing code only when a value is not null.
let Syntax
value?.let {
// this block runs only if value is NOT null
// 'it' refers to the non-null value inside this block
println(it)
}Without let vs With let
val email: String? = "user@example.com"
// Without let
if (email != null) {
println("Sending to: ${email.uppercase()}")
println("Domain: ${email.substringAfter("@")}")
}
// With let — same result, more expressive
email?.let {
println("Sending to: ${it.uppercase()}")
println("Domain: ${it.substringAfter("@")}")
}How ?. let Works
email?.let { ... }
│
└── Is email null?
│
┌────┴────┐
YES NO
│ │
skip run the block
block with it = email (non-null String)
let Returns a Value
let returns the last expression in its block. Combine this with ?: to handle the null case too:
val name: String? = "ravi kumar"
val displayName = name?.let {
it.split(" ").joinToString(" ") { word ->
word.replaceFirstChar { c -> c.uppercase() }
}
} ?: "Anonymous"
println(displayName) // Ravi Kumar
val nullName: String? = null
val display2 = nullName?.let { it.uppercase() } ?: "Anonymous"
println(display2) // Anonymouslet vs if-null-check
Use if when:
You need else (handle both null and non-null cases)
val result = if (x != null) x.process() else defaultValue
Use let when:
You only care about the non-null case
x?.let { doSomethingWith(it) }
Use let + ?: when:
You need both cases but prefer concise style
val result = x?.let { it.process() } ?: defaultValue
Renaming it Inside let
When nesting let calls, rename the parameter to avoid confusion:
val user: String? = "Alice"
val email: String? = "alice@example.com"
user?.let { u ->
email?.let { e ->
println("Sending message to $u at $e")
}
}let for Side Effects
fun saveToDatabase(value: String) {
println("Saved: $value")
}
val input: String? = "New record"
input?.let { saveToDatabase(it) }
// Saved: New record
val nothing: String? = null
nothing?.let { saveToDatabase(it) }
// (nothing happens, saveToDatabase is not called)Practical Example: Notification Sender
data class UserPrefs(val pushToken: String?, val emailAddress: String?)
fun sendPush(token: String, message: String) {
println("PUSH → $token : $message")
}
fun sendEmail(address: String, message: String) {
println("EMAIL → $address : $message")
}
fun notify(prefs: UserPrefs, message: String) {
prefs.pushToken?.let { sendPush(it, message) }
prefs.emailAddress?.let { sendEmail(it, message) }
}
fun main() {
val user1 = UserPrefs("device_abc_123", "alice@mail.com")
val user2 = UserPrefs(null, "bob@mail.com")
val user3 = UserPrefs("device_xyz_789", null)
notify(user1, "Your order has shipped!")
println()
notify(user2, "Your order has shipped!")
println()
notify(user3, "Your order has shipped!")
}Output:
PUSH → device_abc_123 : Your order has shipped!
EMAIL → alice@mail.com : Your order has shipped!
EMAIL → bob@mail.com : Your order has shipped!
PUSH → device_xyz_789 : Your order has shipped!