skip to content

Precondition Functions

require and requireNotNull validate arguments with IllegalArgumentException, while check, checkNotNull, and error report an invalid state with IllegalStateException. Interviewers like that these also smart-cast the checked value, and that their message lambdas are lazy.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What is the difference between require(...) and check(...) in the Kotlin standard library, and which exception does each throw?

level: juniorimportance: must knowfreq 70%

answer

  1. require = argument = IllegalArgumentException
  2. check = state = IllegalStateException
  3. Lazy message lambda runs only on failure
  4. Both inline, both always active
  5. error(msg) = throw IllegalStateException

basics

~10 s

Both check a condition and throw if it is false. require is for bad arguments and throws IllegalArgumentException. check is for a bad object state and throws IllegalStateException.

solid answer

~40 s

Both are inline stdlib guard functions taking a Boolean. require(value) throws IllegalArgumentException when value is false; use it to validate function arguments at the start of a function. check(value) throws IllegalStateException when false; use it to assert that the object or program is in a valid state to proceed. Semantically: require blames the caller (bad input), check blames the current state. Both have an overload taking a lazy message lambda, require(value) { "msg" }, evaluated only on failure. requireNotNull and checkNotNull are the null-checking variants that also smart-cast the value to non-null. Neither is disabled in production (unlike assert). error(msg) is a shorthand that always throws IllegalStateException.

code

kotlin · 4 lines
kotlin
fun transfer(amount: Int, accountOpen: Boolean) {
    require(amount > 0) { "amount must be > 0, was $amount" } // IllegalArgumentException
    check(accountOpen) { "account must be open" }            // IllegalStateException
}

go deeper

for a junior

Knows require throws IllegalArgumentException for bad args and check throws IllegalStateException for bad state.

for a middle

Articulates the caller-vs-state intent and mentions the lazy message overload and that both are always active.

for a senior

Explains inlining (no lambda allocation), how exception type guides catch sites, and pairs the NotNull variants with smart-casting.

for a principal

Frames precondition choice as part of API contract design and error taxonomy, and how it interacts with global exception handling and observability.

## What these functions are Kotlin's standard library (in `kotlin.Preconditions.kt`) provides small `inline` guard functions that throw when a condition is violated. They make intent explicit and produce clear exceptions. ## require — validate arguments `require(condition)` throws `IllegalArgumentException` if `condition` is `false`. Use it for argument validation: the failure means the **caller** passed something invalid. ```kotlin fun withdraw(amount: Int) { require(amount > 0) { "amount must be positive, was $amount" } // ... } ``` `IllegalArgumentException` semantically signals "a method has been passed an illegal argument." ## check — validate state `check(condition)` throws `IllegalStateException` if `condition` is `false`. Use it when the **object's or program's state** is wrong for the operation, regardless of arguments. ```kotlin class Connection { private var open = false fun send(data: String) { check(open) { "connection is not open" } // ... } } ``` ## Choosing between them - `require` -> the **input** is bad -> `IllegalArgumentException`. - `check` -> the **state** is bad -> `IllegalStateException`. This distinction matters because callers may catch these exceptions differently, and the type documents whose fault the failure is. ## Lazy message and inlining Both take an optional `message: () -> Any` lambda evaluated **only on failure**, so building an expensive message string costs nothing on the happy path. Because the functions are `inline`, no lambda object is allocated. ## Not the same as assert Unlike `assert`, `require` and `check` are **always active** in production — they are normal exceptions, not toggled by a JVM flag.

  • Are require and check disabled in release builds like assert can be?
    No. They are ordinary functions that always throw; only assert is gated by the -ea / -enableassertions JVM flag.
  • Why use require instead of writing if (x) throw IllegalArgumentException(...)?
    It is shorter, communicates intent, gives a consistent exception type and message format, and (for the NotNull variants) smart-casts the value.

require checks the ticket you were handed (caller's fault); check confirms the gate is actually open (the system's state).

saying these in an interview costs you the question

  • Saying require throws IllegalStateException (it throws IllegalArgumentException)
  • Claiming require/check are stripped out in production
  • Using check for argument validation or require for state
  • Thinking the message is always evaluated even on success
  • Confusing them with assert

context

open as a page

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

level: middleimportance: must knowfreq 60%

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.

open as a page

What does error(message) do, and why do require/check use a lazy message lambda instead of a plain String parameter?

level: middleimportance: should knowfreq 45%

basics

~10 s

error(msg) always throws an IllegalStateException with that message and never returns. The lazy lambda means the message is only built when the check actually fails, so it costs nothing when everything is fine.

open as a page

How does Kotlin's assert function differ from require/check, and when is it actually evaluated on the JVM?

level: seniorimportance: should knowfreq 35%

basics

~20 s

assert checks an internal sanity condition but can be turned off. On the JVM it only runs when assertions are enabled with the -ea flag. require and check always run. So never use assert to validate real input.

open as a page

How do the Kotlin contracts behind require/check/requireNotNull enable smart-casting, and how would you choose the right precondition function when designing an API?

level: seniorimportance: should knowfreq 30%

basics

~20 s

These functions tell the compiler, via contracts, what is guaranteed if they return without throwing. That lets the compiler narrow types (e.g. to non-null or to a subtype). When designing APIs, pick require for bad inputs, check for bad state, and error for unreachable code.

open as a page