Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin safe call operator usage

Suppose I have the following code :

fun main() {
    val userName: String? = "Dmitry" // line 1
    userName?.takeLast(2)?.reversed() // line 2
    userName?.plus("kotlin").plus("lang") // line 3
}

I can't get the reason for the usage of Elvis operator before reversed() - why it is not possible to use just . ? As I can see in doc takeLast() always provides String, not String?. The same scenario works for Line 3 in my example -> plus() always returns String so there is no need of Elvis before the Second plus() ?

like image 485
Dmitry Avatar asked Dec 31 '25 06:12

Dmitry


2 Answers

Since there is a ?. before takeLast(), the resulting expression evaluates to a nullable String. Since userName could be null, takeLast() might not even be called and you would just have null going into the takeLast() call.

In your last line you are calling an extension function defined for a nullable String receiver, so it doesn’t need to be conditionally called. It’s defined as fun String?.plus so it can be called directly on a nullable input.

By the way, ?. is not called the Elvis operator. It is called the safe call operator. The Elvis operator is ?: and does something different.

like image 172
Tenfour04 Avatar answered Jan 03 '26 10:01

Tenfour04


From the specs:

a?.c is exactly the same as

when (val $tmp = a) {
    null -> null
    else -> { $tmp.c }
}

In other words, userName?.takeLast(2)?.reversed() is the same as:

val $tmp1: String? = when (userName) {
    null -> null
    else -> userName.takeLast(2)
}
val $tmp2: String? = when ($tmp1) {
    null -> null
    else -> $tmp1.reversed()
}
// ...and so on.

The above expansion shows that each step on its own may return null, which means it is necessary to repeat ?..

As for .plus(), the first .plus() is String.plus(), whereas the second .plus() is String?.plus(). The latter would treat null as the string "null", but this is not the case with the first.

If userName is null, userName.plus() will call String?.plus(). It is only because of ?. that String.plus() is called instead. To apply the same aforementioned expansion:

val $tmp: String? = when (userName) {
    null -> null
    else -> userName.plus("kotlin")  // String.plus()
}
$tmp.plus("lang")  // String?.plus()
like image 38
InSync Avatar answered Jan 03 '26 11:01

InSync