We have to cover all branches with all Control-Flow expressions in Kotlin?
Asked Answered
V

2

6

I looked at the docs from Kotlin website, there are only two Control-Flow expressions: if and when.

For if:

the expression is required to have an else branch

For when:

The else branch is evaluated if none of the other branch conditions are satisfied. If when is used as an expression, the else branch is mandatory, unless the compiler can prove that all possible cases are covered with branch conditions.

Question

So it seems that there is no way to make a Control-Flow expression without covering all branches, Is it right? If not, Is there any way to make a Control-Flow expression to miss some branches; If so, why?


Following code will occur if must have both main and 'else' branches if used as an expression

override fun onReceive(context: Context?, intent: Intent?) {
    intent?.let {
        if (it.action == MySDK.BROADCAST_ACTION_LOGIN) {
            mListener.get()?.loggedOn(LoggedOnUserInfo.IT)
        }else if (it.action == MySDK.BROADCAST_ACTION_LOGOUT) {
            // Occur 'if must have both main and 'else' branches if used as an expression'
            mListener.get()?.loggedOut(LoggedOutUserInfo())
        }
    }
}

But following code pass compile.....

override fun onReceive(context: Context?, intent: Intent?) {
    intent?.let {
        if (it.action == MySDK.BROADCAST_ACTION_LOGIN) {
            mListener.get()?.loggedOn(LoggedOnUserInfo.IT)
            context!!.unregisterReceiver(this) // only add this line to test.
        }else if (it.action == MySDK.BROADCAST_ACTION_LOGOUT) {
            mListener.get()?.loggedOut(LoggedOutUserInfo())
        }
    }
}
Vescuso answered 28/11, 2016 at 8:1 Comment(4)
Yes, it's possible, unless the if or when is used as an expression. As in val foo = if (someCondition) "bar" else "baz".Pelorus
in the document, it has "If you're using if as an expression rather than a statement (for example, returning its value or assigning it to a variable), the expression is required to have an else branch." doc, so it seems that when you are using if as statement, it won't require else.Hinayana
so maybe "In Kotlin, if is an expression" is wrong, and "In Kotlin, if can be used both as expression and statement" is correct.Hinayana
@JBNizet and ymonad, thanks you all guys, you are right. But I still have some confuse with this issue, so I add my source code to the question, which make me confuse and lead me have to ask this question.Vescuso
D
13

The trick here is not to use the if as an expression. My guess is that you placed the if at a let block, which returns its last statement, thus using the "result" of the if, thus treating it as an expression.

I suggest throwing away the let function (it is useless anyway here):

override fun onReceive(context: Context?, intent: Intent?) {
    if(intent != null) {
        if (intent.action == MySDK.BROADCAST_ACTION_LOGIN) {
            mListener.get()?.loggedOn(LoggedOnUserInfo.IT)
        } else if (intent.action == MySDK.BROADCAST_ACTION_LOGOUT) {
            mListener.get()?.loggedOut(LoggedOutUserInfo())
        }
    }
}

Your second version compiles because context!!.unregisterReceiver(this) has a different type than mListener.get()?.loggedOut(LoggedOutUserInfo()), which makes the types mismatch and prevents using the if as an expression.

P.S.

Kotlin has quite a few powerful control structures. I personally prefer this version:

override fun onReceive(context: Context?, intent: Intent?) {
    intent ?: return
    when(intent.action) {
        MySDK.BROADCAST_ACTION_LOGIN -> mListener.get()?.loggedOn(LoggedOnUserInfo.IT)
        MySDK.BROADCAST_ACTION_LOGOUT -> mListener.get()?.loggedOut(LoggedOutUserInfo())
    }
}
Dotty answered 28/11, 2016 at 9:1 Comment(2)
I would personally avoid ?: return because it is much harder to distinguish control flow visually. I had a couple of "happy debugging" hours with it.Gaslight
@SerCe really? Could you share your experience (maybe at Slack at #random)? We are kinda in the middle of a fight whether this idiom is appropriate or not.Dotty
G
1

So it seems that there is no way to make a Control-Flow expression without covering all branches, Is it right?

Yes

In the second case

mListener.get()?.loggedOn(LoggedOnUserInfo.IT)
context!!.unregisterReceiver(this)

Is not an expression anymore, the whole if block is a statement. However, you can also provide else with Unit in a first case if you really need an expression:

if (it.action == MySDK.BROADCAST_ACTION_LOGIN) {
    mListener.get()?.loggedOn(LoggedOnUserInfo.IT)
} else if (it.action == MySDK.BROADCAST_ACTION_LOGOUT) {
    // Occur 'if must have both main and 'else' branches if used as an expression'
    mListener.get()?.loggedOut(LoggedOutUserInfo())
} else Unit

But it would be better to avoid this code because it is less readable.

Gaslight answered 28/11, 2016 at 8:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.