kotlinkotlin-contracts

Kotlin contract infer return value instead of argument value


I have a function that looks something like:

fun MyInput?.toOutput() : Output? {
  if (this == null) return null
  return Output(this.someValue)
}

In places where I know that my MyInput is non-null (for example, inside a method that takes a input: MyInput as an arg), I'd like to be able to use input.toOutput as Output instead of Output?

I've tried using

contract {
  returnsNotNull() implies (this@toOutput != null)
}

But that has the implication backwards. That tells me that if toOutput returns a non-null type, that my input was non-null. I want to tell the analyzer things about the return value based on the arguments. In Java, I could use org.jetbrains.annotations.@Contract("null -> null ; !null -> !null") to accomplish this.

Is there a way to do this in Kotlin?


Solution

  • You don't need contracts for this. You just need to make a non-nullable overload. Like this:

    fun MyInput?.toOutput(): Output? {
      if (this == null) return null
      return Output(this.someValue)
    }
    
    fun MyInput.toOutput(): Output = Output(this.someValue)
    

    However, this will not work out of the box on the JVM, because the function signatures will clash. To make it work, you have to give one of the functions a new name with the @JvmName annotation. For example:

    @JvmName("toOutputNonNull")
    fun MyInput.toOutput(): Output = Output(this.someValue)
    

    You will still be able to call it like input.toOutput() from Kotlin, but it will become something like FileNameKt.toOutputNonNull(input) if you call it from Java.