Is it possible to tell the Kotlin compiler that a ...
# getting-started
h
Is it possible to tell the Kotlin compiler that a method returns null only if the input parameter is null? By an annotation or a contract?
y
Copy code
fun foo(x: Type): ReturnType
fun foo(x: Nothing?): ReturnType?
or use
Type?
instead of
Nothing?
h
That's genius, thank you!
e
https://github.com/JetBrains/java-annotations/blob/master/common/src/main/java/org/jetbrains/annotations/Contract.java does support
Copy code
@Contract("null -> null, !null -> !null")
which may be used by IDE inspections, but the Kotlin compiler doesn't make use of it
with the built-in
kotlin.contracts
, you can write
Copy code
@OptIn(ExperimentalContracts::class)
fun foo(x: T?): U? {
    contract {
        returnsNotNull() implies (x != null)
    }
but that probably doesn't give you what you want (
(x != null) implies returnsNotNull()
is not possible)
Youssef's suggestion works for literal
null
, but not for values of nullable type
y
Yes I forgot about that initially, but my suggestion to use
Type?
should therefore do the trick
And the compiler will choose the non-nullable version if the type is smartcast to be not null
e
well, not quite: they both have the same method signature, so you need to disambiguate
h
yeah, I just found that out 😞
I was looking for something like the
@Contract
annotation, but with smartcasts as a result…
y
@JvmName("fooNotNull")
does the trick I believe
e
I'd probably
Copy code
fun foo(x: Any): Any { ... }

@JvmName("nullableFoo")
fun foo(x: Any?): Any? = if (x != null) foo(x) else null
since it's slightly easier to smart-cast to non-nullable to call the right overload, than it is to go the other way, but yeah
y
Depends though since there might be some setup/teardown side-effect that the function does regardless of the nullability of the parameter. Also, if that truly is the case that the function just immediately returns null if the input is null, then it really shouldn't be an extension on nullable, and instead one should use
?.
when calling it
e
well sure, it's pretty useless in this example form, I assume Henning has some other purpose for it
but going the other way
Copy code
fun foo(x: Any?): Any? { ... }

fun foo(x: Any): Any = foo(x as Any?)!!
is just plain uglier IMO
h
I'm just stupid … I use it with generics, and that of course can't work at all:
Copy code
fun <T> newVariable(value: T?): Variable<T>? = value?.let { Variable(it, null, null) }
y
Well... You can, but you just end up specifying the non-null constraint in the type param instead (playground):
Copy code
fun main() {
    val kotlin = "🙂"
    println(newVariable(kotlin))
    val nullableKotlin: String? = null
    println(newVariable(nullableKotlin))
    val nullableKotlin2: String? = kotlin
    println(newVariable(nullableKotlin2))
}
data class Variable<T>(val value: T, val second: String?, val third: Int?)
@JvmName("newVariableNullable")
fun <T> newVariable(value: T?): Variable<T>? = value?.let { Variable<T>(it, null, null) }.also { print("nullable: ") }
fun <T: Any> newVariable(value: T): Variable<T> = Variable(value, null, null).also { print("non null: ") }
prints:
non null: Variable(value=🙂, second=null, third=null)
nullable: null
nullable: Variable(value=🙂, second=null, third=null)
🙏 1