== 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]