As I learn new components in Kotlin, I came accross requireNotNull
and checkNotNull
but the only difference I've found is that requireNotNull
can throw an IllegalArgumentException
while checkNotNull
can throw an IllegalStateException
. Is this the only reason why there are two methods, or I'm missing some under-the-hood implementation detail?
It is a semantic difference and hence it throws different exceptions. RequireNotNull is used to check input values, typically at the beginning of a method, while checkNotNull is used anywhere to check the current state.
The exception types are the only practical difference, as far as the compiler is concerned — but there's a big difference in intent, for anyone reading the code:
• require…()
functions are for checking parameters, to confirm that a function's input fulfils its contract. So you'd normally call them first thing in a function. (Of course, Kotlin's non-nullable types mean that you wouldn't need to call requireNotNull()
for a single parameter; but you might need to check a more complex condition on a combination of parameters or their sub-objects.) That's why they throw IllegalArgumentException
: it's checking that the arguments are legal.
• check…()
functions are for checking the relevant properties, to confirm that the object or whatever is in a valid state for this function to be called now. (Again, any properties that were never null would be typed accordingly, so checkNotNull()
is more appropriate for cases where a property, combination, and/or sub-property can be null, but this function mustn't be called when they are.) So they throw IllegalStateException
: they're checking that the object's current state allows the function to be called.
In both cases, you could of course write a standard if
check (as you would in Java). Or you could use the Elvis operator ?:
to do the check the first time the possibly-null value is used. But these functions give you an alternative that's in a more declarative form: you'd normally put them at the top of the function, where they spell out what the function's contract is, in a way that's obvious to anyone glancing at the code.
As a linked answer points out, there are also assert…()
functions, which again have more of a semantic difference than a practical one. Those are for detecting programming errors away from the boundary of a function call: for confirming invariants and other conditions, and for all the checks in unit tests and other automated tests.
(Assertions have another important difference: they can be enabled and disabled from the command-line. Though in my experience, that's not a very good thing. If a check is important, it should always be run: be mandatory; if not, then it should be removed, or at least moved to automated tests, once the code is debugged.)
It is a semantic difference and hence it throws different exceptions. RequireNotNull is used to check input values, typically at the beginning of a method, while checkNotNull is used anywhere to check the current state.
If you're looking for differences in implementation, the best place to go would be the source code. In this case it seems like there are no differences aside from the different exception thrown, the source for both methods is otherwise identical.
checkNotNull
[...]
if (value == null) {
val message = lazyMessage()
throw IllegalStateException(message.toString())
} else {
return value
}
requireNotNull
[...]
if (value == null) {
val message = lazyMessage()
throw IllegalArgumentException(message.toString())
} else {
return value
}
Therefore the difference is purely semantic. The answer from @gidds details some good scenarios for using them both.
© 2022 - 2024 — McMap. All rights reserved.