251

In Kotlin is there an equivalent to the Swift code below?

if let a = b.val {

} else {

}
Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
iadcialim24
  • 3,817
  • 5
  • 21
  • 32

16 Answers16

483

You can use the let-function like this:

val a = b?.let {
    // If b is not null.
} ?: run {
    // If b is null.
}

Note that you need to call the run function only if you need a block of code. You can remove the run-block if you only have a oneliner after the elvis-operator (?:).

Be aware that the run block will be evaluated either if b is null, or if the let-block evaluates to null.

Because of this, you usually want just an if expression.

val a = if (b == null) {
    // ...
} else {
    // ...
}

In this case, the else-block will only be evaluated if b is not null.

marstran
  • 26,413
  • 5
  • 61
  • 67
  • 66
    It isn't equivalent. `a` cannot be accessed inside `let` and `run` block. – Jacky Choi Nov 10 '17 at 01:57
  • 12
    `a` isn't defined yet inside the scope of `let` and `run`. But you can access the value of `b` inside the `let` with the implicit parameter `it`. This serves the same purpose I guess. – marstran Nov 10 '17 at 07:37
  • 1
    The swift code completes the assignment of `a` before those blocks of code. And `a` can be accessed inside. – Jacky Choi Nov 10 '17 at 08:04
  • 1
    What does it assign to `a`? If it's `b`, then `it` serves the exact same purpose. Does it reassign `a` with the result of the `val`-block? – marstran Nov 10 '17 at 08:28
  • Then, in kotlin, no assignment is needed? – Jacky Choi Nov 10 '17 at 09:18
  • 4
    You can only access `it` from within the `let` block. The result of the `let` or `run` block will be assigned to `a`. The result is the last expression in the block. You use the assignment _after_ `let`/`run` has finished, just like you do with any other normal assignment. – marstran Nov 10 '17 at 09:48
  • 2
    This idiom-to-idiom translation is actually wrong. The Swift code doesn't result in the symbol `a` being defined after the `if let` expression, it is defined just for the `then` clause of `if let`. – Marko Topolnik Feb 18 '18 at 09:39
  • 1
    Another way these two aren't equivalent is in thread safety. In the second version, if `b` is a property, it can be changed (by another thread) after the null check, but before it is accessed in the block. For this reason, I would recommend either taking a copy of `b` to a local `val` to work on, or to simply use the `let` version. – aaaidan Feb 20 '18 at 03:20
  • 6
    Should use `also` instead of `let` or `run` will execute if `let` return null: `b?.let { null } ?: run { /* will always execute */ }` – Love Mar 17 '18 at 16:05
  • 1
    @Love That would change the semantics of the statement. The `also` function will return `b` itself if it's not null, not what the lambda returns. – marstran Mar 19 '18 at 13:50
  • @marstran Take a look at [the other answer](https://stackoverflow.com/a/48850409/1020871) *note that `a` is bound only within the then-block*. – Love Mar 20 '18 at 07:50
  • @Love Yes, that is what is discussed earlier in this comment chain. This answer is not to be regarded as a one-to-one translation of the Swift code. It is simply how you would express it in Kotlin. – marstran Mar 20 '18 at 11:47
  • 2
    The reason of existence of `if let` is precisely the scope control it provides. It allows you to capture the result of an optional-typed expression without exporting it to the rest of the function body. This is why an example such as `b?.let ...` doesn't demonstrate its purpose: if you have that in your code, you can just as well write `if (b != null) { ... }` In Swift, if you have `if let b { ... }` you can just as well have `if b != nil { ... }` No language would introduce special syntax just for that. – Marko Topolnik Jun 15 '18 at 12:52
  • Note that the `run` after the elvis operator is not required. You can have a single operation after the elvis operator without a run statement. `?: return` for an instance is perfectly valid. – Zoe Apr 13 '19 at 21:01
  • @marstran, the last sentence does not make sense. Should it not read "the `else`-block will only be evaluated if `b` is **not** null"? – ordonezalex Feb 17 '20 at 09:48
  • poorly implemented optionals example. swift if let construction does not work like that. it creates a new variable with optional unwrapped. – JBarros35 Sep 25 '20 at 15:51
  • @JBarros35 As I have explained in the comments above, the answer is not to be regarded as a 1-to-1 translation of the Swift code. It is a translation into idiomatic Kotlin code. The unwrapped variable can be accessed inside the `let`-block with `it`. – marstran Sep 25 '20 at 16:02
  • use it else block of let: inline fun R?.orElse(block: () -> R): R { return this ?: block() } – gturedi Apr 02 '21 at 09:12
108

Let's first ensure we understand the semantics of the provided Swift idiom:

if let a = <expr> {
     // then-block
}
else {
     // else-block
}

It means this: "if the <expr> results in a non-nil optional, enter the then-block with the symbol a bound to the unwrapped value. Otherwise enter the else block.

Especially note that a is bound only within the then-block. In Kotlin you can easily get this by calling

<expr>?.also { a ->
    // then-block
}

and you can add an else-block like this:

<expr>?.also { a ->
    // then-block
} ?: run {
    // else-block
}

This results in the same semantics as the Swift idiom.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • 5
    `also` is the closest match to `if let`, thank you. – Leon Jan 07 '21 at 14:13
  • 2
    Great answer, as @Leon said, this is the closest to the Swift `if let`. Thank you for the explanation - this should be the accepted answer. `also` should be used rather than the `let`, since Kotlin `let` returns a value... Thanks! – Sakiboy Jan 29 '21 at 20:05
  • 1
    use it else block of let: inline fun R?.orElse(block: () -> R): R { return this ?: block() } – gturedi Apr 02 '21 at 09:13
  • 1
    Can't we change this to be the accepted answer? "also" is way better then "let" in this case – Oliver Hoffmann Jan 15 '23 at 12:40
18

My answer is totally a copy cat from the others. However, I cannot understand their expression easily. So I guess it would be nice to provide an more understandable answer.

In swift:

if let a = b.val {
  //use "a" as unwrapped
}
else {

}

In Kotlin:

b.val?.let{a -> 
  //use "a" as unwrapped
} ?: run{
  //else case
}
Wu Yuan Chun
  • 1,116
  • 11
  • 11
  • 10
    Using `let` instead of `also` means that the `run` block will execute whenever the `let` block returns `null`, which is not what we want. – Marko Topolnik Sep 03 '19 at 07:31
14

if let statement

Swift if let Optional Binding (so called if-let statement) is used to extract a non-optional value if one exists, or to do nothing if a value is nil.

Swift's if-let statement:

let b: Int? = 50

if let a: Int = b {
    print("Good news!")
} else {
    print("Equal to 'nil' or not set")
}

/*  RESULT: Good news!  */

In Kotlin, like in Swift, to avoid crashes caused by trying to access a null value when it’s not expected, a specific syntax (like b.let { } in second example) is provided for properly unwrapping nullable types:

Kotlin's equivalent1 of Swift's if-let statement:

val b: Int? = null
val a = b

if (a != null) { 
    println("Good news!")
} else { 
    println("Equal to 'null' or not set")
}

/*  RESULT: Equal to 'null' or not set  */

Kotlin’s let method, when used in combination with the safe-call operator ?:, provides a concise way to handle nullable expressions.

Kotlin's inline let function and Elvis Operator of Swift's nil coalescing operator:

val b: Int? = null

val a = b.let { nonNullable -> nonNullable } ?: "Equal to 'null' or not set"
println(a)

/*  RESULT: Equal to 'null' or not set  */


guard let statement

guard-let statement in Swift is simple and powerful. It checks for some condition and if it evaluates to be false, then the else statement executes which normally will exit a method.

Let's explore a Swift's guard-let statement:

let b: Int? = nil

func method() {
    guard let a: Int = b else {
        print("Equal to 'nil' or not set")
        return
    }
    print("Good news!")
}
method()

/*  RESULT: Equal to 'nil' or not set  */

Kotlin's similar effect of Swift's guard-let statement:

Unlike Swift, in Kotlin, there is no guard statement at all. However, you can use the Elvis Operator?: for getting a similar effect.

val b: Int? = 50

fun method() {
    val a = b ?: return println("Equal to 'null' or not set")
    return println("Good news!")
}
method()

/*  RESULT: Good news!  */
Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
  • it does not unwrap the optional and created another variable based on it. its not a let valid construction. – JBarros35 Sep 25 '20 at 15:53
  • Sorry, what construction you mean? – Andy Jazz Sep 25 '20 at 15:57
  • if let a {} else {} the way you put there you are just executing stuff but there are no variable unwrapped inside to be used! Swift unwraps the variable for programmers and Kotlin its just poor in terms of optionals. – JBarros35 Sep 25 '20 at 16:05
  • 1
    This also works: `foo?.let { /* statements here */ } ?: run { /* statements for else condition */ }` – P. Ent Oct 28 '22 at 15:21
12

there are two answers above, both got a lot acceptances:

  1. str?.let{ } ?: run { }
  2. str?.also{ } ?: run { }

Both seem to work in most of the usages, but #1 would fail in the following test:

enter image description here

#2 seems better.

Sean
  • 2,967
  • 2
  • 29
  • 39
  • 2
    This is the correct answer! Do not use variable?.let { doStuff() } ?: run { doOtherStuff() } Because of doStuff returns null then the run block will also run. Also will return the value passed into the lambda so it will always return a value if there was a n on null value. – Matt Wolfe Oct 22 '20 at 01:24
8

Unlike Swift, Its not necessary to unwrap the optional before using it in Kotlin. We could just check if the value is non null and the compiler tracks the information about the check you performed and allows to use it as unwrapped.

In Swift:

if let a = b.val {
  //use "a" as unwrapped
} else {

}

In Kotlin:

if b.val != null {
  //use "b.val" as unwrapped
} else {

}

Refer Documentation: (null-safety) for more such use cases

Shahzin KS
  • 1,001
  • 8
  • 8
  • 3
    This only works if b is a local variable. What about class variables? – Warpzit Apr 03 '19 at 09:17
  • This totally doesn't apply in the general case (ie: when variables are class variables or are references from other classes. You really do need to create a snapshot copy of its variable and use that immutable value in the 'then' clause. Kotlin really needs this added to the language. – Marchy Jan 14 '21 at 15:32
6

Here's how to only execute code when name is not null:

var name: String? = null
name?.let { nameUnwrapp ->
    println(nameUnwrapp)  // not printed because name was null
}
name = "Alex"
name?.let { nameUnwrapp ->
    println(nameUnwrapp)  // printed "Alex"
}
  • if name == null code in { } don't execute, if name as String - code execute : nameUnwrapp == name. – Александр Яковенко Mar 22 '18 at 08:20
  • 2
    I don't think this is what OP is asking, I am sure OP already knows what `let` does, but the question is about the `else` strategy that executes if the object on which `let` is called is `null`. Swift has it in more natural (arguable) way. But having done both Kotlin and Swift a lot, I do wish Kotlin had a simple unwrapping like Swift does. – Harshad Kale Apr 21 '18 at 20:41
2

Here's my variant, limited to the very common "if not null" case.

First of all, define this somewhere:

inline fun <T> ifNotNull(obj: T?, block: (T) -> Unit) {
    if (obj != null) {
        block(obj)
    }
}

It should probably be internal, to avoid conflicts.

Now, convert this Swift code:

if let item = obj.item {
    doSomething(item)
}

To this Kotlin code:

ifNotNull(obj.item) { item -> 
    doSomething(item)
}

Note that as always with blocks in Kotlin, you can drop the argument and use it:

ifNotNull(obj.item) {
    doSomething(it)
}

But if the block is more than 1-2 lines, it's probably best to be explicit.

This is as similar to Swift as I could find.

noamtm
  • 12,435
  • 15
  • 71
  • 107
1

Probably I am very late however the easiest way to unwrap and option is

yourOptionalString ?: return

this was all the following lines will have unwrapped string

Syed Ali Salman
  • 2,894
  • 4
  • 33
  • 48
0

There is a similar way in kotlin to achieve Swift's style if-let

if (val a = b) {
    a.doFirst()
    a.doSecond()
}

You can also assigned multiple nullable values

if (val name = nullableName, val age = nullableAge) {
    doSomething(name, age)
}

This kind of approach will be more suitable if the nullable values is used for more than 1 times. In my opinion, it helps from the performance aspect because the nullable value will be checked only once.

source: Kotlin Discussion

Richard
  • 21
  • 5
  • 3
    Please be aware that the approach you're showing does not compile currently. It is only a suggestion on how to alter the Kotlin language in order to deal with this issue. Hence, this is not a valid solution. – Peter F Nov 19 '18 at 15:41
0

I'm adding this answer to clarify the accepted answer because it's too big for a comment.

The general pattern here is that you can use any combination of the Scope Functions available in Kotlin separated by the Elvis Operator like this:

<nullable>?.<scope function> {
    // code if not null
} :? <scope function> {
    // code if null
}

For example:

val gradedStudent = student?.apply {
    grade = newGrade
} :? with(newGrade) {
    Student().apply { grade = newGrade }
}
Sir Codesalot
  • 7,045
  • 2
  • 50
  • 56
  • `let if` is used to make sure that you have a *non-null* value of your nullable variable, which is exactly what this solution does. And in a more efficient way because it doesn't create an extra variable. – Sir Codesalot Sep 29 '20 at 07:32
0

The cleanest option in my opinion is this

Swift:

if let a = b.val {

} else {

}

Kotlin

b.val.also { a ->

} ?: run {

}
Stiiv
  • 21
  • 1
0

Swift if let statement in Kotlin

The short answer is use simple IF-ELSE as by the time of this comment there is no equivalent in Kotlin LET,

    if(A.isNull()){
// A is null
    }else{
// A is not null
    }
bastami82
  • 5,955
  • 7
  • 33
  • 44
  • @JBarros35 correct, that is exactly what I said too lol, but I gave the next best option to what is available in the Kotlin. – bastami82 Sep 25 '20 at 19:07
0

we can get the same Unwraping syntax like Swift if let using inline fun

inline fun <T:Any?> T?.unwrap(callback: (T)-> Unit) : Boolean {
    return if (this != null) {
        this?.let(callback)
        true
    }else {
        false
    }
}

Uses: :

        val  name : String? = null
        val  rollNo : String? = ""
        var namesList: ArrayList<String>?  = null

        if (name.unwrap { name ->

                Log.i("Dhiru", "Name have value on it  $name")

            })else if ( rollNo.unwrap {
                Log.i("Dhiru","Roll have value on it")

            }) else if (namesList.unwrap {  namesList  ->
                Log.i("Dhiru","This is Called when names list have value ")
            })  {
             Log.i("Dhiru","No Field have value on it ")
        }
Dhiru
  • 3,040
  • 3
  • 25
  • 69
0

The problem with the Any?.let {} ?: run {} constructions is that:

  • It only allows for one non-null check per statement
  • If the let block returns null the run block is evaluated anyway
  • It's not possible to perform multiple checks in a switch/when style

A solution which tackles most of these problems is to define functions like the following:

private inline fun <A> ifNotNull(p1: A?, block: (A) -> Unit): Unit? {
    if (p1 != null) {
        return block.invoke(p1)
    }
    return null
}

private inline fun <A, B> ifNotNull(p1: A?, p2: B?, block: (A, B) -> Unit): Unit? {
    if (p1 != null && p2 != null) {
        return block.invoke(p1, p2)
    }
    return null
}

private inline fun <A, B, C> ifNotNull(p1: A?, p2: B?, p3: C?, block: (A, B, C) -> Unit): Unit? {
    if (p1 != null && p2 != null && p3 != null) {
        return block.invoke(p1, p2, p3)
    }
    return null
}

This would allow for a statement like:

ifNotNull(a, b) { a, b ->
    // code when a, b are not null
} ?:
ifNotNull(c) { c ->
    // code when a, b are null and c not null
} ?: 
ifNotNull(d, e, f) { d, e, f ->
    // code when a, b, c are null and d, e, f not null
} ?: run {
    // code which should be performed if a, b, c, d, e and f are null
}

The only caveat is that continue and break statements are not supported if executed within a loop compared to Swift's if let equivalent.

Werner Altewischer
  • 10,080
  • 4
  • 53
  • 60
-1

If b is a member variable then this approach seems most readable to me:

val b = this.b
if (b == null) {
    return
}
println("non nullable : ${b}")

This is also consistent with how it works in swift, where a new local variable shadows the member variable.

Steve Vermeulen
  • 1,406
  • 1
  • 19
  • 25