基于可变值的 Kotlin 和惯用的编写方式,'如果不是 null,否则......'

Posted

技术标签:

【中文标题】基于可变值的 Kotlin 和惯用的编写方式,\'如果不是 null,否则......\'【英文标题】:Kotlin and idiomatic way to write, 'if not null, else...' based around mutable value基于可变值的 Kotlin 和惯用的编写方式,'如果不是 null,否则......' 【发布时间】:2018-02-03 06:03:42 【问题描述】:

假设我们有这样的代码:

class QuickExample 

    fun function(argument: SomeOtherClass) 
        if (argument.mutableProperty != null ) 
            doSomething(argument.mutableProperty)
         else 
            doOtherThing()
        
    

    fun doSomething(argument: Object) 

    fun doOtherThing() 


class SomeOtherClass 
    var mutableProperty: Object? = null

与在 Java 中不同的是,在 Java 中,您可能会独自担心在运行时取消引用 null,这不会编译 - 非常正确。当然,mutableProperty 在“if”中可能不再为空。

我的问题是处理这个问题的最佳方法是什么?

一些选项是显而易见的。在不使用任何新的 Kotlin 语言功能的情况下,最简单的方法显然是将值复制到方法范围内,该值随后不会更改。

有这个:

fun function(argument: SomeOtherClass) 
    argument.mutableProperty?.let 
        doSomething(it)
        return
    
    doOtherThing()

这有一个明显的缺点,即您需要提前返回或避免执行后续代码 - 在某些小型上下文中可以,但有异味。

那么就有这种可能:

fun function(argument: SomeOtherClass) 
    argument.mutableProperty.let 
        when 
            it != null -> 
                doSomething(it)
            
            else -> 
                doOtherThing()
            
        
    

虽然它的目的更清晰,但可以说它比 Java 风格的处理方式更加笨拙和冗长。

我是否遗漏了什么,是否有一个首选的成语来实现这一点?

【问题讨论】:

IMO 我真的认为简单的with(argument.mutableProperty) if (this != null) a(this) else b() 就足够简洁了(或与let 相当)。通常这应该不是什么大问题,而且仍然很短。跨度> 我觉得不错,谢谢!我建议您将其添加为答案 - 至少,这是实现它的另一种方式。 【参考方案1】:

我认为没有真正“短”的方法来实现它,但是您可以简单地在withlet 中使用条件:

with(mutableVar)  if (this != null) doSomething(this) else doOtherThing() 
mutableVar.let  if (it != null) doSomething(it) else doOtherThing() 

事实上,“捕获”一个可变值是let的主要用例之一。

这相当于您的when 声明。

总是有你描述的选项,将它分配给一个变量:

val immutable = mutableVar

if (immutable != null) 
    doSomething(immutable)
 else 
    doOtherThing()

这始终是一个很好的后备方案,例如事情变得太冗长了。

可能没有真正的很好方法来实现这一点,因为只允许将 last lambda 参数放在() 之外,因此指定两个不会真正适合所有其他标准函数的语法。

如果您不介意,可以写一个(或者如果您将传递方法引用):

inline fun <T : Any, R> T?.ifNotNullOrElse(ifNotNullPath: (T) -> R, elsePath: () -> R)
        = let  if(it == null) elsePath() else ifNotNullPath(it) 

...

val a: Int? = null
a.ifNotNullOrElse( println("not null") ,  println("null") )

请注意,我个人不会这样做,因为这些自定义构造都不是很悦耳的阅读。 IMO:坚持使用let/run,必要时回退到if-else

【讨论】:

【参考方案2】:

更新:

正如 franta 在 cmets 上提到的,如果方法 doSomething() 返回 null,则将执行 elvis 运算符右侧的代码,这可能不是大多数人想要的情况。但同时,在这种情况下,doSomething() 方法很可能只会做某事而不会返回任何内容。

还有一个替代方案:正如 protossor 在 cmets 上提到的那样,可以使用 also 而不是 let,因为 also 返回 this 对象而不是函数块的结果。

mutableProperty?.also  doSomething(it)  ?: doOtherThing()

原答案:

我会将let 与Elvis operator 一起使用。

mutableProperty?.let  doSomething(it)  ?: doOtherThing()

来自文档:

如果 ?: 左边的表达式不为空,elvis 运算符 返回它,否则将表达式返回到右边。笔记 仅当左侧表达式时才计算右侧表达式 边为空。

右侧表达式后面的代码块:

   mutableProperty?.let 
            doSomething(it)
         ?: run 
            doOtherThing()
            doOtherThing()
        

【讨论】:

这样做的缺点似乎是限制了 RHS 上的单个方法调用,而不是代码块。我在这方面错过了什么技巧吗? @RobPridham 我想你可以在技术上使用run 并在那里使用 lambda,但这是一个延伸 是的。正如@1blustone 所提到的,可以使用runlet。或者你可以只写一个函数块并通过调用.invoke() 或在关闭的lambda 后面加上() 来调用它,但是这个函数不会被内联。 我不明白为什么这个答案有这么多的赞成票。您提出了一些建议,并且在下一节中您将证明您的建议是错误的。再读一遍文档 -> 如果mutableProperty 不为空,它会跳转到doSomething(it),如果此方法返回null,则elvis 运算符执行doOtherThing() 天哪。真是一团糟。【参考方案3】:

添加自定义内联函数如下:

inline fun <T> T?.whenNull(block: T?.() -> Unit): T? 
    if (this == null) block()
    return this@whenNull


inline fun <T> T?.whenNonNull(block: T.() -> Unit): T? 
    this?.block()
    return this@whenNonNull

那么你可以这样写代码:

var nullableVariable :Any? = null
nullableVariable.whenNonNull 
    doSomething(nullableVariable)
.whenNull 
    doOtherThing()

【讨论】:

【参考方案4】:

我通常是这样写的:

  takeIfsomecondition?.alsoput somecondition is met code?:runput your else code here

注意 takeIf 后面的问号是必须的。您也可以使用或应用关键字。

【讨论】:

【参考方案5】:

你也可以这样做:

class If<T>(val any: T?, private val i: (T) -> Unit) 
    infix fun Else(e: () -> Unit) 
        if (any == null) e()
        else i(any)
    

然后你可以像这样使用它:

If(nullableString) 
   //Use string
 Else 


【讨论】:

【参考方案6】:

怎么样:

argument.mutableProperty
  ?.let  doSomething(it)  
  ?: doOtherThing()

【讨论】:

这可能在某些情况下有效,但如果例如doSomething() 返回一个值。在这种情况下,它也会调用 doOtherThing(),如果 doSomething() 返回 null【参考方案7】:

我通常只是这样做:

when(val it=argument.mutableProperty) 
    null -> doOtherThing()
    else -> doSomething(it)

【讨论】:

【参考方案8】:

感谢@zyc zyc,现在我使用此代码

inline fun <T> T?.ifNull(block: () -> Unit): T? 
    if (this == null) block()
    return this@ifNull


inline fun <T> T?.ifNonNull(block: (T) -> Unit): T? 
    this?.let(block)
    return this@ifNonNull

// use
xxxx.ifNull 
    // todo
.ifNonNull 
    // todo

【讨论】:

以上是关于基于可变值的 Kotlin 和惯用的编写方式,'如果不是 null,否则......'的主要内容,如果未能解决你的问题,请参考以下文章

在 Kotlin 中处理可为空或空列表的惯用方式

惯用的 Kotlin2JS Gradle 设置

Kotlin集合操作 ⑤ ( Map 集合 | 获取 Map 值 | Map 遍历 | 可变 Map 集合 )

Kotlin集合操作 ⑤ ( Map 集合 | 获取 Map 值 | Map 遍历 | 可变 Map 集合 )

在 Java 中编写 URL 或 URI 的惯用方式是啥?

Kotlin语言的惯用法