It is a common pattern to validate required preconditions at the beginning of a function or block. There are two different kinds of preconditions:
IllegalArgumentException should be thrown if these preconditions are violated. 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.
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.
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.
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.
Replace |
With |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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().
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
}
fun argumentPreconditions(argument: Int?, limit: Int?) {
requireNotNull(argument) // Compliant
requireNotNull(limit) // Compliant
require(argument >= 0) // Compliant
require(argument < limit) {"Argument < $limit"} // Compliant
}
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
}
}
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
}
}