【问题标题】:What's the idiomatic Kotlin way to write `if...else null` as part of ?./?: chains?将 `if...else null` 作为 ?./?: 链的一部分编写的 Kotlin 惯用方式是什么?
【发布时间】:2019-09-20 00:10:12
【问题描述】:

(为清楚起见进行了编辑)

我发现自己一直在写 (if (x) f() else null) ?: (if (y) g() else null) ?: (if (z) h() else null) 和类似的东西,而且我确定我不是唯一一个人ץ

我总是在寻找像下面定义的valueIf 这样的函数(这是takeIf,参数的顺序颠倒了,所以值是惰性的),这样我就可以编写像getViewEventType2() 这样的代码,而不是像@ 这样的代码987654326@(均在下文详述)。

有没有我遗漏的成语?

(另外,编译器对这样的函数是否聪明,或者我应该担心创建太多的临时闭包?)

    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

【问题讨论】:

  • 这里的例子和链接的文章可以帮助你:stackoverflow.com/a/48466874/44615
  • 我不明白 - 你到底在问什么?这两个似乎都很好。
  • @MartinStone 链接的文章是关于 Kotlin 处理可空值调用链的精彩故事。我在问如何将条件组合到这些链中。 func().takeIf{condition} 可以工作,但不会短路(它会反转你的代码)。
  • 因为我一直发现自己在写(if (x) f() else null) ?: (if (y) g() else null) ?: (if (z) h() else null) 和类似的东西,而且我确定我不是唯一一个

标签: kotlin idioms


【解决方案1】:

Wangs answer 所示:takeIf 是此类构造的必经之路。

我会把剩下的留给任何感兴趣的人(因为我的评论已经应用了):你可能想省略 let 以支持 safe operator ?.。此外,您可能还对destructing declarations 感兴趣,我认为这使其更具可读性:

fun Intent.getViewEventType() : String? = takeIf { it.action == "android.intent.action.VIEW" }
        ?.`data`?.pathSegments
        ?.takeIf { it.size == 3 }
        ?.takeIf { (source, actionType) -> // destructuring in action... you may want to name the variables appropriately
            source == "app" && actionType == "event"
        }
        ?.get(2)

我在这里也使用了single expression function,但我把它留给你。由于您在此处链接了很多内容,因此您可能至少希望在使用此类构造时指定返回类型(您也可以省略)。

【讨论】:

    【解决方案2】:

    你仍然可以使用takeIf() 来实现你想要的,就像这里在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)
    }
    

    由 OP 编辑​​:这是我使用的最终代码:

    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)
    }
    

    【讨论】:

    • 哦,这感觉很地道,谢谢。所以诀窍是从我要挖掘的值开始(intent),然后开始过滤和转换它(intent.takeIf{...}?.let{it...}?.takeIf{...})。我不认为我的代码会按意图执行,但确实如此。
    • 没错,你明白了。并且使用?. 将确保返回第一个null 结果而不评估链的其余部分。
    • 例如(if (x) f() else null) ?: (if (y) g() else null) ?: (if (z) h() else null) 将转换为 f.takeIf{x}?.let{it()} ?: g.takeIf{y}?.let{it()} ?: h.takeIf{z}?.let{it()}(或与 0.takeIf{x}?.let{f()} 相同)
    • 你甚至可能想在这里省略let...例如:fun Intent.getViewEventType() = takeIf { it.action == "android.intent.action.VIEW" } ?.`data`?.pathSegments ?.takeIf { it.size == 3 &amp;&amp; it[0] == "app" &amp;&amp; it[1] == "event" } ?.get(2)...当然至少有适当的新行;-)
    • @Roland 当然,我相应地编辑了答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-17
    • 2023-03-14
    • 1970-01-01
    • 2018-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多