I have a class with a nullable property description: String?
, and for convenience would like to expose a fun hasDescription(): Boolean
that null-checks said String.
For even more convenience, I am hoping to use a contract so I can smart cast description
to non-nullable String
after checking hasDescription()
.
- Is this possible now (with experimental contracts, and Kotlin 1.5)?
- Might it be possible when contracts go stable?
data class Widget (
val description: String? = null,
)
class Gadget {
val widget: Widget? = null
val description: String? get() = widget?.description
@ExperimentalContracts
fun hasDescription(): Boolean {
// Error in contract description: only references to
// parameters are allowed in contract description
contract {
returns(true) implies((this@Gadget).description is String)
}
return this.description != null
}
}
@ExperimentalContracts
fun testContract(gadget: Gadget) {
if (gadget.hasDescription()) {
// Error: Type mismatch (String? for String)
// expected, since the contract itself does not compile
val description: String = gadget.description
}
}
In short, it's not possible.
And yes, it might be implemented in the future.
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.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
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 aString?
, thenWidget
could implementdescription
as a non-nullableString
. This works because in Kotlin the nullable type is a super-type of the non-nullable type. Any implementation ofdescription
can choose to be either beString?
orString
.