Throwing exceptions in Kotlin
Using the require, check, and error functions
In programming, it's common to write an if
statement to check a precondition, for example:
fun doSomething(value: Int) {
if (value < 0) {
throw IllegalArgumentException("value cannot be negative")
}
// do something
}
Kotlin offers some helper functions to make your code more concise.
The require function
We can rewrite the code above in the following way:
fun doSomething(value: Int) {
require(value > 0) { "value cannot be negative" }
// do something
}
The require function helps validate function arguments. If the condition isfalse
, it will throw an IllegalArgumentException
with the message provided in the lambda.
The requireNotNull function
This function has a self-explanatory name. It will check if the value is null; if it's false, it will throw an IllegalArgumentException
with the message provided in the lambda. See the example:
fun plusOne(number: Int?) : Int {
requireNotNull(number) { "number cannot be null" }
return number + 1
}
The smart cast works in this scenario. This is why we can sum the number in the second line without worrying about null values.
The check function
The check
function has the same signature as the require
function, but it will throw an IllegalStateException
instead. This function is not used to validate parameters. You should use it to validate the internal state of a class. See this example:
class Queue {
private val queue = ArrayDeque<String>(10)
fun enqueue(id: String) {
check(queue.size == 10) { "Queue is full" }
queue.push(id)
}
fun dequeue(): String {
check(queue.isEmpty()) { "Queue is empty" }
return queue.pop()
}
}
If any of the validations fail, an IllegalStateException
will be thrown.
The checkNotNull function
This works the same way as the requireNotNull
function, but it throws an IllegalStateException
instead of an IllegalArgumentException.
This function is to be used to validate null
inside the state of a class, just like the check
function. Example:
data class User(
val id: String,
val name: String,
val email: String,
)
interface UserRepository {
fun findById(): User?
}
interface EmailService {
fun sendEmail(email: String, message: String)
}
class SendEmailUseCase(
private val userRepository: UserRepository,
private val emailService: EmailService,
) {
fun sendMessage(message: String) {
val user = userRepository.findById()
checkNotNull(user) { "User not found" }
emailService.sendEmail(user.email, message)
}
}
The error function
The error
function always throws an IllegalStateException
when it's called. This is an example of the error
function in action. It's common to see it used together with the Elvis operator.
class SendEmailUseCase(
private val userRepository: UserRepository,
private val emailService: EmailService,
) {
fun sendMessage(message: String) {
val user = userRepository.findById()
?: error("User not found")
emailService.sendEmail(user.email, message)
}
}
Another common usage is in the when expression:
fun processMessage(message: Message): String {
return when (message.type) {
"info" -> "INFO: ${message.message}"
"warning" -> "WARNING: ${message.message}"
"error" -> "ERROR: ${message.message}"
else -> error("Unknown message type ${message.type}")
}
}
Conclusion
As we can see, Kotlin offers some functions to validate preconditions that can make your code less verbose and more idiomatic. The functions require
and check
have the same signature but throw different exceptions and have different purposes.
Do you think you have what it takes to be one of us?
At WAES, we are always looking for the best developers and data engineers to help Dutch companies succeed. If you are interested in becoming a part of our team and moving to The Netherlands, look at our open positions here.
WAES publication
Our content creators constantly create new articles about software development, lifestyle, and WAES. So make sure to follow us on Medium to learn more.
Also, make sure to follow us on our social media:
LinkedIn — Instagram — Twitter — YouTube