Syntax and null safety
vals and vars, expression-oriented control flow, and the type system that keeps null out of your code until you ask for it.
Values, functions, control flow
val name: String = "Ada" // immutable reference
var count = 0 // mutable, type inferred as Int
fun greet(who: String, greeting: String = "Hello"): String =
greeting + ", " + who
// if and when are expressions, so they produce values
val label = if (count == 0) "empty" else "n items"
val grade = when (count) {
0 -> "F"
in 1..49 -> "D"
in 50..89 -> "B"
else -> "A"
}
for (i in 1..5 step 2) print(i) // 1 3 5
listOf(1, 2, 3).forEach { println(it) }- Prefer
val. Avarshould be a deliberate exception, which makes concurrency reasoning much easier. - The last expression in a function body is the return value when you use
=instead of braces. whenwith an exhaustive set of branches needs noelsewhen the compiler can prove coverage, for example over an enum or a sealed type.
Nullability is in the type
In Kotlin String and String? are different types. The compiler refuses to call anything on a nullable value until you have handled the null case, so the check happens at compile time rather than at runtime.
| Operator | Meaning | Result when the value is null |
|---|---|---|
s.length | Direct access, only on non-null types | Compile error on a nullable |
s?.length | Safe call | null |
s ?: "default" | Elvis, supply a fallback | The fallback value |
s!!.length | Assert non-null | Throws NullPointerException |
s?.length ?: 0 | Safe call plus fallback | Zero |
x as? Int | Safe cast | null instead of throwing |
data class User(val email: String, val phone: String?)
fun contact(user: User?): String {
val email = user?.email ?: return "unknown"
val phone = user.phone?.takeIf { it.isNotBlank() } ?: "no phone"
return email + " / " + phone
}
// let runs the block only for a non-null value
user?.let { sendWelcome(it.email) }
// smart cast after an explicit check
if (user != null) println(user.email.length)⚠️
Each
!! is a promise the compiler cannot verify, and one broken promise crashes the app. Reserve it for values you have just created, and prefer ?:, requireNotNull or a lateinit-free design everywhere else.FAQ
What is a platform type?
A value coming from Java has no nullability information, so Kotlin treats it as
String! and lets you use it as either. It is not a compile-time guarantee, so validate or annotate at the boundary.val or lateinit var?
Use
val with a constructor parameter whenever possible. lateinit var is for framework-injected fields you genuinely cannot pass in, and it throws if read before assignment.Related
Classes, data classes and extensions Coroutines and collections
Last refreshed 2026-09-18.