29

I'm trying to find out how to achieve the combination of "if let + cast" in kotlin:

in swift:

if let user = getUser() as? User {
   // user is not nil and is an instance of User
}

I saw some documentation but they say nothing regarding this combination

https://medium.com/@adinugroho/unwrapping-sort-of-optional-variable-in-kotlin-9bfb640dc709 https://kotlinlang.org/docs/reference/null-safety.html

dor506
  • 5,246
  • 9
  • 44
  • 79

5 Answers5

41

One option is to use a safe cast operator + safe call + let:

(getUser() as? User)?.let { user ->
    ...
}

Another would be to use a smart cast inside the lambda passed to let:

getUser().let { user ->
    if (user is User) {
        ...
    }
}

But maybe the most readable would be to just introduce a variable and use a smart cast right there:

val user = getUser()
if (user is User) {
    ...
}
Alexander Udalov
  • 31,429
  • 6
  • 80
  • 66
  • 1
    I think you are using a "safe call" (`?. `) and not the "elvis operator" (`?:`) in your first example. – Michael Hoff May 29 '17 at 21:44
  • 8
    Clearly the if let syntax Swift added was genius, these choices are poor and more poor.. – Rob Oct 02 '17 at 22:55
  • You might need to replace "as?" with "as" to skip use assertion operator !! inside let (getUser() as? User)?.let { user -> user!! ... } might be: (getUser() as User)?.let { user -> user ... } – Ahmad Abdullah Nov 26 '19 at 14:27
  • Contrary to @Rob, I find the Swift syntax to be the poorest option. It requires understanding the concept of an `if` has been overloaded from a boolean check to a nil/optional check and assignment side-effect. Plus the scope of the assignment looks wrong -- the assignment positioning prior to the opening brace would suggest the assignment is available in the `else` also! The Kotlin approaches all require no new syntax knowledge over commonly used operators in regular old Kotlin, and at very little to no additional "cost" in extra length over the Swift version. – Raman Sep 12 '22 at 19:52
  • BTW, since the introduction of `also` into Kotlin, this is a better answer: https://stackoverflow.com/a/48850409/430128. The `let` returns the value of the `then` block, so it isn't the same as the Swift idiom, especially not if an `else` is intended on nil via a trailing elvis operator. – Raman Sep 12 '22 at 20:23
  • @raman great counter arguments, especially the one about not more characters. If let is so great how come no one ever remembers how to use it? Kotlin is so full of little self-impressed optimizations that their echo chamber liked. Frankly I think optionals in both languages (Swift and Kotlin) are a mess. – Rob Sep 27 '22 at 19:30
  • 1
    @Rob Well we wouldn't be programmers if we weren't spending at least some of our time alternately bitching about or pimping one language or another :-) Cheers. – Raman Sep 29 '22 at 13:01
7

Kotlin can automatically figure out whether a value is nil or not in the current scope based on regular if statements with no need for special syntax.

val user = getUser()

if (user != null) {
    // user is known to the compiler here to be non-null
}

It works the other way around too

val user = getUser()

if (user == null) {
    return
}

// in this scope, the compiler knows that user is not-null 
// so there's no need for any extra checks
user.something 
nhaarman
  • 98,571
  • 55
  • 246
  • 278
hasen
  • 161,647
  • 65
  • 194
  • 231
7

In Kotlin you can use the let:

val user = getUser()?.let { it as? User } ?: return

This solution is closest to guard but it may be useful.

mgonzalez
  • 107
  • 2
4

In Kotlin you can use:

(getUser() as? User)?.let { user ->
  // user is not null and is an instance of User
}

as? is a 'safe' cast operator that returns null instead of throwing an error on failure.

nhaarman
  • 98,571
  • 55
  • 246
  • 278
2

What about this one?

val user = getUser() ?: return
Djangow
  • 341
  • 3
  • 9