ChatGPT解决这个技术问题 Extra ChatGPT

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

假设我们有这样的代码:

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

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

有几个选项是显而易见的。在不使用任何新的 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 相当)。通常这不应该是一个大问题,而且这仍然很短。
我觉得不错,谢谢!我建议您将其添加为答案-至少,这是实现它的另一种方式。

C
Cumbayah

更新:

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

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

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

原答案:

我会将 letElvis operator 一起使用。

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

从文档:

如果 ?: 左边的表达式不为 null,则 elvis 运算符返回它,否则返回右边的表达式。请注意,仅当左侧为空时才计算右侧表达式。

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

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

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

我不相信有一个真正“短”的方法来实现它,但是您可以简单地在 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()
}

如果事情变得过于冗长,这总是一个很好的后备。

可能没有真正的 nice 方法来实现这一点,因为只允许将 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


b
borjab

关于什么:

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

这在某些情况下可能有效,但如果 doSomething() 返回一个值,则可能会出现问题。在这种情况下,如果 doSomething() 返回 null,它也会调用 doOtherThing()
z
zyc zyc

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

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

M
Mathias Henze

我通常只是这样做:

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

j
j2emanue

我通常这样写:

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

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


a
aotian16

感谢@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
}

s
sunilson

你也可以这样做:

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 {

}