Search code examples
kotlinidioms

What's the idiomatic Kotlin way to write `if...else null` as part of ?./?: chains?


(Edited for clarity)

I keep finding myself writing (if (x) f() else null) ?: (if (y) g() else null) ?: (if (z) h() else null) and similar and I'm sure I'm not the only oneץ

I'm always reaching for a function like valueIf defined below (which is takeIf with order of parameters reversed so the value is lazy) , which would let me write code like getViewEventType2() instead of code like getViewEventType1() (both elaborated below).

Is there an idiom I'm missing?

(Also, is the compiler smart about functions like this, or should I fear creating too many temporary closures?)

    private fun getViewEventType1(): String? {
        return if (intent.action == "android.intent.action.VIEW") {
            intent.data?.pathSegments?.let {
                if (it.size == 3 && it[0] == "app" && it[1] == "event") it[2]
                else null
            }
        } else null
    }

    private fun getViewEventType2(): String? {
        return valueIf(intent.action == "android.intent.action.VIEW") {
            intent.data?.pathSegments?.let {
                valueIf(it.size == 3 && it[0] == "app" && it[1] == "event") { it[2] }
            }
        }
    }

    inline fun <T> valueIf(condition: Boolean, func: () -> T?) =
            if (condition) func() else null

Solution

  • You can still use takeIf() to achieve what you want, like shown here in an extension function on Intent:

    fun Intent.getViewEventType(): String? {
        return takeIf { it.action == "android.intent.action.VIEW" }
            ?.`data`
            ?.pathSegments
            ?.takeIf { it.size == 3 && it[0] == "app" && it[1] == "event" }
            ?.get(2)
    }
    

    Edit by OP: this is the final code I went with:

    fun getViewEventType(): String? {
        return intent.takeIf { it.action == "android.intent.action.VIEW" }
                ?.`data`
                ?.pathSegments
                ?.takeIf { it.size == 3 && it[0] == "app" && it[1] == "event" }
                ?.get(2)
    }