Kotlin contract infer return value instead of argument value
Asked Answered
L

1

6

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?

Lumbago answered 25/4, 2019 at 16:24 Comment(3)
Note that for this, you might as well not offer MyInput?.toOutput() at all. Just provide MyInput.toOutput(), and then let callers write myInput?.toOutput() with the question mark. That'll make the nullability behavior much more obvious to users as well as letting you avoid this entire issue.Evangelical
@LouisWasserman That's a great idea, but would it work to call toOutput from Java?Lumbago
It would work in the sense that you'd have to check for null on the Java end.Evangelical
L
9

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.

Loculus answered 25/4, 2019 at 16:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.