Can Kotlin declare variable in if statement?

Viewed 475

I have a piece of code:

class Test {
    fun test() {
        val fruit = if (Apple().doSomeThing() != null) {//1
            val result = Apple().doSomeThing()       //2
            //...use result
        } else if (Banana().doSomeThing() != null) {
            Banana().doSomeThing()
        } else {
            Peach().doSomeThing()
        }

    }

    internal open class Fruit

    internal class Apple : Fruit() {

        fun doSomeThing(): Boolean? {
            return false
        }
    }

    internal class Banana : Fruit() {
        fun doSomeThing(): Boolean? {
            return false
        }
    }

    internal class Peach : Fruit() {

        fun doSomeThing(): Boolean? {
            return false
        }
    }
}

It call Apple().doSomeThing() twice, can i declare a variable in if statement like this:?

var result :Boolean? = null
if((result = Apple().doSomeThing()) != null){
     //result.xxxx  //use result directly
}

And,how can i write test() method more gracefully!!

4 Answers

You can use the let function (doc) to avoid calling Apple().doSomething() twice and chain them with elvis operator ?: as below

    val fruit = Apple().doSomeThing()?.let {
        if (it) { // use the value of Apple().doSomething() here
            
        } else {
            
        }
    } ?: Banana().doSomeThing()?.let {
        if (it) {
            
        } else {
            
        }
    } ?: Peach().doSomeThing()?.let {
        if (it) {
            
        } else {
            
        }
    }

it within the scope of let in Apple().doSomeThing()?.let { } would have the value of Apple().doSomeThing() when it is not equal to null. And if Apple().doSomeThing() is equal to null, the elvis operator ?: would enable evaluating the next expression, that is Banana().doSomeThing()?.let { } and so on.

Nope, but you can do so inside of when. The thing to note is that x is immutable.

when (val x = foo()) { ... }

First, a nicer version of test():

fun test() {
    val foo = Apple().doSomeThing()?.let { result ->
        //... use result
    } ?: Banana().doSomeThing()?.let { result ->
        //...use result
    } ?: Peach().doSomeThing()
}

Second, if you don't need to check all of the fruits in order, you can make much better use of inheritance:

fun test() {
    val fruit = listOf(Apple(), Banana(), Peach()).random()
    val result = fruit.doSomeThing()
}

interface Fruit {
    fun doSomeThing(): Boolean?
}

class Apple : Fruit {
    override fun doSomeThing(): Boolean? = false
}

class Banana : Fruit {
    override fun doSomeThing(): Boolean? = false
}

class Peach : Fruit {
    override fun doSomeThing(): Boolean? = false
}

As an addendum to the other answers: in the case where you're not doing anything else with the return values, it can simplify down to:

fun test() {
    val fruit = Apple().doSomeThing()
             ?: Banana().doSomeThing()
             ?: Peach().doSomeThing()

}
Related