skip to content

How do requireNotNull and checkNotNull work, and what do they do to the type of the value afterwards?

level: middleimportance: must knowfreq 60%

answer

  1. requireNotNull -> IllegalArgumentException; checkNotNull -> IllegalStateException
  2. Both RETURN the value as non-null (T from T?)
  3. Contract returns() implies (value != null) -> smart-cast
  4. Better than !! (message + right exception type)
  5. Lazy message, inline, no allocation

basics

~10 s

They throw if the value is null, otherwise they return the same value as a non-null type. So after calling them you can use the value without a null check.

solid answer

~30 s

requireNotNull(value) throws IllegalArgumentException if value is null; checkNotNull(value) throws IllegalStateException if null. Crucially both are declared to RETURN the value as a non-null type (T from T?), so the idiom is val x = requireNotNull(nullable). They are also annotated with Kotlin contracts (returns() implies (value != null)), which lets the compiler smart-cast the original variable to non-null after the call even when you ignore the return value. Both take a lazy message lambda. Prefer them over the !! operator because they give a meaningful message and the right exception semantics (bad argument vs bad state) instead of a bare NullPointerException.

code

kotlin · 6 lines
kotlin
fun loadUser(id: String?, session: Session?): User {
    val realId = requireNotNull(id) { "id must not be null" }   // IllegalArgumentException
    checkNotNull(session) { "session not initialized" }          // IllegalStateException
    // session smart-cast to non-null below thanks to the contract
    return repository.find(realId, session.token)
}

go deeper

for a junior

Knows they throw on null and let you use the value without a further null check.

for a middle

Distinguishes the two exception types, knows they return the non-null value, and prefers them over !! for messages.

for a senior

Explains the Kotlin contract returns()-implies clause enabling smart-cast even when the return value is discarded, and stability requirements.

for a principal

Discusses contracts as an API-design tool, smart-cast stability rules across mutable/delegated state, and consistent null-failure taxonomy across a codebase.

## The two functions - `requireNotNull(value)` -> throws `IllegalArgumentException` if `value` is `null` (bad argument). - `checkNotNull(value)` -> throws `IllegalStateException` if `value` is `null` (bad state). ## Two ways they remove nullability **1. Return type.** Each is generic and returns the value as a non-null type: ```kotlin public inline fun <T : Any> requireNotNull(value: T?): T ``` So you can capture the narrowed value: ```kotlin val name: String = requireNotNull(maybeName) { "name required" } ``` **2. Kotlin contract -> smart-cast.** Both functions carry a `contract { returns() implies (value != null) }`. A *contract* is a stdlib mechanism that tells the compiler what is guaranteed if the function returns normally. Here it guarantees the argument is non-null, so the compiler **smart-casts** the original variable even if you discard the return value: ```kotlin fun greet(name: String?) { requireNotNull(name) println(name.length) // name smart-cast to String, no ?. needed } ``` *Smart-cast* = the compiler automatically treats a variable as a more specific (here non-null) type after a proven check. ## Why prefer them over !! The `!!` operator also asserts non-null but throws a bare `NullPointerException` with no message and the wrong semantic flavor. `requireNotNull` / `checkNotNull` give a descriptive lazy message and the correct exception type that says **whose fault** the null is. ## Lazy message The message lambda `{ ... }` runs only when the value is actually null, so it is free on the happy path, and because the functions are `inline` no lambda object is allocated.

  • If you ignore the return value of requireNotNull, can you still use the variable as non-null?
    Yes, for a stable variable (e.g. a local val) the contract smart-casts it to non-null after the call.
  • When would smart-cast NOT apply even after requireNotNull?
    If the value is a mutable var that could change concurrently, or a custom getter / delegated property the compiler cannot prove is stable.

saying these in an interview costs you the question

  • Saying they return Unit / do not return the value
  • Claiming checkNotNull throws IllegalArgumentException
  • Treating them as identical to !! with no benefits
  • Not knowing about the contract-based smart-cast
  • Believing the message is evaluated even when value is non-null

context