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()
?
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.
1
From the specs:
a?.c
is exactly the same aswhen (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()