Kotlin和惯用的写法,'if not not null,else ......'基于可变值

时间:2017-08-25 08:17:35

标签: android kotlin idioms

假设我们有这段代码:

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中不同的是,您可以单独担心在运行时空取消引用,这不会编译 - 非常正确。当然,{if}中的mutableProperty可能不再为null。

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

有一些选择是显而易见的。在不使用任何新的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风格的方式更加笨拙和冗长。

我错过了什么,是否有一个首选的习惯用来实现这个目标?

7 个答案:

答案 0 :(得分:31)

<强>更新

正如franta在评论中提到的,如果方法doSomething()返回null,那么将执行elvis运算符右侧的代码,这可能不是大多数情况下的理想情况。但与此同时,在这种情况下,doSomething()方法很可能只会执行某些操作而不返回任何内容。

另一种选择:正如protossor在评论中提到的那样,可以使用also而不是let,因为also会返回this个对象,而不是mutableProperty?.also { doSomething(it) } ?: doOtherThing() 功能块。

let

原始回答:

我会将mutableProperty?.let { doSomething(it) } ?: doOtherThing() Elvis operator一起使用。

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

来自doc:

  

如果?:左边的表达式不为null,则为elvis运算符   返回它,否则返回右边的表达式。注意   只有左手才能评估右侧表达式   方是空的。

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

NSString *result = [webView stringByEvaluatingJavaScriptFromString:@"document.body.offsetHeight;"];

NSInteger height = [result integerValue];

答案 1 :(得分:12)

我不相信有一个真正的&#34;短&#34;实现它的方法,但您可以在withlet

中使用条件
with(mutableVar) { if (this != null) doSomething(this) else doOtherThing() }
mutableVar.let { if (it != null) doSomething(it) else doOtherThing() }

事实上,&#34;捕获&#34;可变值是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") })

答案 2 :(得分:1)

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

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

答案 3 :(得分:1)

关于:

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

答案 4 :(得分:0)

我通常这样写:

  takeIf{somecondition}?.also{put somecondition is met code}?:run{put your else code here}

在takeIf为必填项后注意问号。您还可以使用或应用关键字。

答案 5 :(得分:0)

您还可以执行以下操作:

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 :(得分:0)

我通常只是这样做:

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