== Why is this an issue?

It is a common pattern to validate required preconditions at the beginning of a function or block. There are two different kinds of preconditions:

* Preconditions about argument values. An example is the assertion that a function argument lies within a specific range. An `IllegalArgumentException` should be thrown if these preconditions are violated.
* Preconditions about the state of the owner or the execution context of the function. An example is when a specific method, such as `open`, `init` or `prepare`, must be called before the current method can be executed. An `IllegalStateException` should be thrown if these preconditions are violated.

The Kotlin standard library provides the functions `check()`, `require()`, `checkNotNull()` and `requireNotNull()` for this purpose.
They should be used instead of directly throwing an `IllegalArgumentException` or an `IllegalStateException`.

=== What is the potential impact?

==== Readability and Understanding

This change makes it easier to understand the code
because the semantics of `check()`, `require()`, `checkNotNull()` and `requireNotNull()`,
as well as the fact that this is a preconditions check,
are evident to the reader.
When developers share common standards and idioms, they need to spend less effort understanding each other's code.

==== Code Redundancy

Using a built-in language feature or a standard API is always better than a custom implementation,
because the reimplementation of something that already exists is unnecessary.

==== Consistency

When  `check()`, `require()`, `checkNotNull()` and `requireNotNull()` are used in an idiomatic way,
there is more consistency in what kind of exception is thrown in which situation.

== How to fix it

[cols="3,2"]
|===
| **Replace**
| **With**

| `if (!condition) throw IllegalArgumentException()`
| `require(condition)`

| `if (reference == null) throw IllegalArgumentException()`
| `requireNotNull(reference)`

| `require(reference != null)`
| `requireNotNull(reference)`

| `if (!condition) throw IllegalStateExceptionException()`
| `check(condition)`

| `if (reference == null) throw IllegalStateException()`
| `checkNotNull(reference)`

| `check(reference != null)`
| `checkNotNull(reference)`

| `throw IllegalStateException()`
| `error()`
|===

A constructor function for the exception message can be provided
as an optional argument
for `check()`, `require()`, `checkNotNull()` and `requireNotNull()`.
This means the message is constructed only if the exception is thrown.
For the `error()` function, an optional error message parameter can be provided directly.
That is, without a parameter, because an exception is unconditionally thrown by `error()`.

=== Code examples

==== Noncompliant code example

[source,kotlin,diff-id=1,diff-type=noncompliant]
----
fun argumentPreconditions(argument: Int?, limit: Int?) {
    if (argument == null) throw IllegalArgumentException() // Noncompliant, replace with requireNotNull
    require(limit != null)  // Noncompliant, replace with requireNotNull
    if (argument < 0) throw IllegalArgumentException()  // Noncompliant, replace with require
    if (argument >= 0) throw IllegalArgumentException("Argument < $limit") // Noncompliant, replace with require
}
----

==== Compliant solution

[source,kotlin,diff-id=1,diff-type=compliant]
----
fun argumentPreconditions(argument: Int?, limit: Int?) {
    requireNotNull(argument) // Compliant
    requireNotNull(limit) // Compliant
    require(argument >= 0) // Compliant
    require(argument < limit) {"Argument < $limit"} // Compliant
}
----

==== Noncompliant code example

[source,kotlin,diff-id=2,diff-type=noncompliant]
----
fun statePreconditions() {
    if (state == null) throw IllegalStateException() // Noncompliant, replace with checkNotNull
    check(ioBuffer != null)  // Noncompliant, replace with checkNotNull
    if (state < 0) throw IllegalStateException()  // Noncompliant, replace with check
    if (state == 42) throw IllegalStateException("Unknown question") // Noncompliant, replace with check

    when(state) {
        0..10 -> processState1()
        11..1000 -> processState2()
        else -> throw IllegalStateException("Unexpected state $state") // Noncompliant, replace with error
    }
}
----

==== Compliant solution

[source,kotlin,diff-id=2,diff-type=compliant]
----
fun statePreconditions() {
    checkNotNull(state) // Compliant
    checkNotNull(ioBuffer) // Compliant
    check(state >= 0) // Compliant
    check(state != 42) {"Unknown question"} // Compliant

    when(state) {
        0..10 -> processState1()
        11..1000 -> processState2()
        else -> error("Unexpected state $state") // Compliant
    }
}
----

== Resources

=== Documentation

* https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/require.html[Kotlin API Docs, require]
* https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/check.html[Kotlin API Docs, check]

=== Articles & blog posts

* https://bignerdranch.com/blog/write-better-code-using-kotlins-require-check-and-assert/[Jeremy W. Sherman, Write Better Code Using Kotlin’s Require, Check and Assert]
