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. A var should 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.
  • when with an exhaustive set of branches needs no else when 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.

OperatorMeaningResult when the value is null
s.lengthDirect access, only on non-null typesCompile error on a nullable
s?.lengthSafe callnull
s ?: "default"Elvis, supply a fallbackThe fallback value
s!!.lengthAssert non-nullThrows NullPointerException
s?.length ?: 0Safe call plus fallbackZero
x as? IntSafe castnull 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.

Classes, data classes and extensions Coroutines and collections

Last refreshed 2026-09-18.