In short, it's not possible.
Contracts primarily consider the behavior of methods rather than the properties of values. Properties of values should be handled by the type system rather than contracts.
And yes, it might be implemented in the future.
It doesn't mean that if something potentially can be handled by the type system, then it is out of contracts' scope.
https://github.com/Kotlin/KEEP/blob/master/proposals/kotlin-contracts.md#scope-and-restrictions
Alternatives
Nulls are well handled in Kotlin's type system, and so we can use that to check for nullability.
Smart casting
Using smart-casting would remove the need for Contracts and the hasDescription()
method.
fun nullCheckWhen(gadget: Gadget) {
when (val description = gadget.description) {
null -> println("description is null")
else -> println("description.length ${description.length}")
}
}
fun earlyReturn(gadget: Gadget) {
val description = gadget.description ?: return
println("description.length ${description.length}")
}
fun forcefulNullCheck(gadget: Gadget) {
val description = requireNotNull(gadget.description) {
"description must not be null"
}
println("description.length ${description.length}")
}
fun elvisScopeFunctionNullSafe(gadget: Gadget) {
gadget.widget?.description?.let { description ->
println("description.length ${description.length}")
}
}
These are a little more clunky, and not as pretty as Contracts could be - but at least they work.
In fact, some of them use Kotlin Contracts, but of course they can only work on method parameters.
Preconditions.kt
/**
* Throws an [IllegalArgumentException] if the [value] is null. Otherwise returns the not null value.
*/
@kotlin.internal.InlineOnly
public inline fun <T : Any> requireNotNull(value: T?): T {
contract {
returns() implies (value != null)
}
return requireNotNull(value) { "Required value was null." }
}
Type hierarchies
I don't think this approach is applicable to your situation, but I thought I'd share it as it can be useful.
If description
was defined in an interface as a String?
, then Widget
could implement description
as a non-nullable String
. This works because in Kotlin the nullable type is a super-type of the non-nullable type. Any implementation of description
can choose to be either be String?
or String
.
interface Component {
val description: String?
}
data class Widget(
override val description: String
) : Component
data class Greeble(
override val description: String?
) : Component
fun nullCheckGeneric(component: Component) {
when (component) {
is Widget ->
// no need for a null check
println("description.length ${component.description.length}")
is Greeble ->
// description is nullable
println("description.length ${component.description?.length}")
}
}