Kotlin Cheatsheet

Null Safety

Use this Kotlin reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Nullable types

val name: String = "Ada"        // never null
val nickname: String? = null    // nullable — must be handled before use
OperatorMeaningExample
?.safe call — evaluates to null if receiver is nulluser?.email?.length
?:Elvis — fallback when left side is nullnickname ?: "anon"
!!assert non-null, throws NullPointerException if nullcache!!.size
as?safe cast — null instead of ClassCastExceptionx as? String
?.let { }run block only when non-nullid?.let { load(it) }

!! is a code smell outside tests and truly-impossible cases — prefer ?:, ?.let, or requireNotNull.

Idiomatic patterns

// run only when non-null; `it` is smart-cast to non-null inside
user?.let { send(it.email) }

// Elvis with early exit — throw / return are expressions
val id = request.id ?: return
val cfg = load() ?: error("config missing")

// chain with default
val city = user?.address?.city ?: "unknown"

// null-safe equality needs no operators: == handles null
if (a == b) { }        // true when both null

Preconditions

FunctionThrowsUse for
require(cond) { "msg" }IllegalArgumentExceptionvalidating arguments
requireNotNull(x) { "msg" }IllegalArgumentExceptionnon-null argument; returns non-null x
check(cond) { "msg" }IllegalStateExceptionvalidating internal state
checkNotNull(x)IllegalStateExceptionnon-null state; returns non-null x
error("msg")IllegalStateExceptionunreachable branches
fun process(order: Order?) {
    val o = requireNotNull(order) { "order required" }   // o: Order
    check(o.items.isNotEmpty()) { "empty order" }
}

Smart casts

fun describe(value: Any?): String {
    if (value == null) return "null"          // value: Any from here on
    if (value is String) return "len=${value.length}"   // auto-cast to String
    if (value !is Int) return "other"
    return "int=${value + 1}"                 // value: Int
}

Smart casts work after is/!is/null checks on vals the compiler can prove stable (locals, private vals). For var properties, copy to a local first: val v = this.field.

lateinit

class Service {
    lateinit var repo: Repo          // var, non-null type, no primitive types

    fun ready() = ::repo.isInitialized

    fun use() {
        repo.save()                   // throws UninitializedPropertyAccessException if unset
    }
}

Use lateinit for DI/framework-injected properties. For computed-once values prefer val x by lazy { ... } instead.

Nulls in collections

val xs: List<Int?> = listOf(1, null, 3)

xs.filterNotNull()                    // [1, 3]           List<Int>
xs.mapNotNull { it?.times(2) }        // map + drop nulls
strings.firstNotNullOfOrNull { it.toIntOrNull() }   // first parse that succeeds
listOfNotNull(a, b, c)                // builds list skipping null args
map.getOrDefault(key, 0); map[key] ?: 0

Platform types (Java interop)

Values from un-annotated Java APIs have unknown nullability (String!). The compiler lets you treat them either way — crashes surface at runtime. Pin the type at the boundary:

val header: String? = javaRequest.getHeader("X-Id")   // choose nullable explicitly