The ensure
function is a Kotlin extension function that allows you to throw a custom exception when a condition is not
met. It behaves identically to the regular require
function,
but it can be parameterized with a custom exception type instead of always throwing an IllegalArgumentException
upon
condition failure.
Such a feature allows for more specific error handling, as you can decide which exception type you want to throw based on the condition that failed, while maintaining the same syntax.
require | ensure |
// throws IllegalArgumentException
requireNotNull(owner)
// (smart cast to owner)
// throws IllegalArgumentException
require(owner.id > 0) { "Invalid owner id" } |
// throws OwnerNotFoundException
ensureNotNull<OwnerNotFoundException>(owner)
// (smart cast to owner)
// throws InvalidOwnerIdException
ensure<InvalidOwnerIdException>(owner.id > 0) {
"Invalid owner id"
} |
- ✅ Ability to throw custom exceptions using the same syntax as the regular
require
function; - ✅ It can be upper bounded to a specific exception type. An example could be a
sealed class
hierarchy where you want to ensure that only this hierarchy of exceptions can be thrown.When changing the function(s) parameterized generic type from// example of sealed class hierarchy sealed class DomainException(message: String) : Throwable(message) class InBoundsExceptionA(message: String) : DomainException(message) class InBoundsExceptionB(message: String) : DomainException(message) // an exception type that is not part of the sealed class hierarchy class OutOfBoundsException(message: String) : Exception(message)
reified T : Throwable
toreified T : DomainException
, then:// no compile-time error ensure<InBoundsExceptionA>(true) // compile-time error ensure<OutOfBoundsException>(true)
- ❌ The exception type to be thrown must have a primary constructor that takes a single
String
parameter, which can be a limitation in some cases;- Example:
class InvalidOwnerIdException(message: String) : Trowable(message)
- Example:
- ❌ When a condition is not met, reflection is used to instantiate the exception type. It won't have a significant impact on performance in most cases, but it's something to be aware of;
- ❌ The extension functions opt in to the ExperimentalContracts, which, as the name suggests, is an experimental feature.
Important
To minimize performance overhead, we plan to integrate KSP into the project and extend its functionality to be available in Kotlin Multiplatform.