Computer scienceProgramming languagesKotlinAdvanced featuresDSL

Require, check, and assert

5 minutes read

When programming, we often need to make continuous checks in our code. For example, we might need to check what preconditions we need to have to be able to execute a piece of code, or what happens if a fragment has been executed, or whether the result is what we want or expect.

With other languages, this can become somewhat tedious, as we need to resort to the use of conditionals and manual exception throwing. Meanwhile, Kotlin uses its own constructs to make our work easier. In Kotlin, require, check, and assert are functions used to verify conditions in code. These functions throw exceptions if the condition they check is not met. However, each of them has a specific use and purpose.

Throughout this topic, you will learn to develop safer and more reliable code. Let's get to it!

require

fun require(value: Boolean) is used to verify the requirements of the arguments of a function or a constructor. If the condition specified in require is not met (is false), an IllegalArgumentException is thrown.

Here we have a traditional construction using if and throw. We use it to check the prerequisites or a parameter of a function.

fun setAgeWithIf(age: Int) {
    if (age < 0) {
        throw IllegalArgumentException("Age must not be less than zero")
    }
    println("Age is $age")
}

Now, we will use require to solve this type of task and check a prerequisite for executing a fragment of code.

fun setAgeWithRequire(age: Int) {
    require(age >= 0) { "Age must not be less than zero" }
    println("Age is $age")
}

In both cases, IllegalArgumentException will be thrown if the age is less than zero. However, the version with require is more concise and makes the code easier to read and understand.

check

fun check(value: Boolean) is used to verify the state of an object or variable. It throws IllegalStateException if the condition specified in check is not met (is false), so we need a legal state to perform the code.

Here's an example of how you might do that with an if statement and throw:

class MyClassWithIf {
    private var isInitialized = false

    fun performActionWithIf() {
        if (!isInitialized) {
            throw IllegalStateException("Object state is not initialized")
        }
        println("Action performed")
    }
}

And now let's see how you can do it with check:

class MyClassWithCheck {
    private var isInitialized = false

    fun performActionWithCheck() {
        check(isInitialized) { "Object state is not initialized" }
        println("Action performed")
    }
}

assert

fun assert(value: Boolean) is a function in Kotlin used to verify conditions in the code that are supposed to be true unless there is a bug in the program. If the condition specified in assert is not met (false), an AssertionError is thrown. However, assertions are disabled by default and can be enabled by passing the -ea (enable assertions) option to the Java Virtual Machine (JVM).

Now, let's see an example using if and throw:

fun calculateValueWithIf(x: Int): Int {
    val value = x * 2
    if (value != x + x) {
        throw AssertionError("Error in calculation")
    }
    return value
}

In the above case, if the result of the calculation is incorrect due to a bug in the program, AssertionError will be thrown with the message "Error in calculation".

Here's an example of how you might use assert:

fun calculateValueWithAssert(x: Int): Int {
    val value = x * 2
    assert(value == x + x) { "Error in calculation" }
    return value
}

In this case, AssertionError will be thrown if the calculation is incorrect. As you can see, the former version is more verbose and less concise than the assert version. It's important to mention that assert is primarily used during development and testing to find and diagnose bugs in the program. It is not recommended to use assert to handle error situations at runtime in a production program.

Good practice

Here's the procedure you might want to follow when performing your checks:

  • Begin by using check to verify the initial state of the object. If these checks fail, it means the function should not have been invoked in the first place due to incorrect object state.
  • Next, use require to validate the function arguments. If any argument is invalid, it's best to identify that before proceeding with the function's operations, as the function call will eventually fail due to the invalid argument.
  • After validating the state and arguments, proceed with the main logic or operations of your function.
  • Finally, use assert to ensure that your function has done its job correctly. This could involve verifying that certain objects have been updated to a new state or ensuring that the return value is as expected.
class MyClass {
    private var isInitialized = false

    fun initialize() {
        isInitialized = true
    }

    fun performAction(x: Int, y: Int): Int {
        check(isInitialized) { "Object is not initialized" }
        require(x > 0 && y > 0) { "Both x and y must be greater than zero" }

        val result = x + y

        assert(result == x + y) { "Error in calculation" }

        return result
    }
}

In the above example:

  • check is used to ensure that the object has been initialized before performAction is called.
  • require is used to validate that both x and y are greater than zero.
  • The main logic of the function is to add x and y together.
  • assert is used to verify that the result of the addition is correct.

Such an approach helps ensure that your functions are robust and behave as expected under different conditions, improving the reliability of your code.

Conclusion

In this topic, we have learned how to use require, check, and assert to make more concise, secure, and readable code thanks to Kotlin syntax. Are you ready for some tasks?

13 learners liked this piece of theory. 2 didn't like it. What about you?
Report a typo