在 Kotlin 中检查表达式是否为 null,类似 Lua?

Lua 中(在 Corona SDK 开发之外很少使用),您可以使用以下方式在 if 语句中评估 ANY 表达式:

  1. 如果表达式为 null,则返回 false
  2. 如果表达式是布尔值 false,则返回 false
  3. 其他情况将返回 true

举例

if (1) // true
if ("Hello World") // true
if (instanceOfSomeRandomClass) // true
if ( [2, null, "foo"] ) // true
if (thisFunctionCallWithReturnNull()) // false
if (0 == 1) // false

在 Kotlin 中,if 也恰好是一个表达式,因此可以组合这些特性,我可以想到很多创造性的使用方式。

在 Kotlin 中是否有等效的方法?

我知道您可以始终手动检查 if (expression != null),这样做没有问题,但 if (expression) 更加“懒惰”,我喜欢偷懒 :)

点赞
用户4616087
用户4616087

引用 Kotlin 语言规范

条件表达式的类型必须是 kotlin.Boolean 的子类型,否则就是错误的。

因此,不,Kotlin 中没有直接的等效方法。

不过,您可以通过在 Any? 上使用自定义的 扩展函数 来模拟该行为:

fun Any?.toBoolean() = this != null && this != false

在下方代码中可以看到它的效果(请注意 input.toBoolean()):

fun main() {
    evaluate(1)
    evaluate("Hello World")
    evaluate(String())
    evaluate(listOf(2, null, "foo"))
    evaluate(null)
    evaluate(0 == 1)
}

fun evaluate(input: Any?) {
    println("$input is ${input.toBoolean()}")
}

输出结果为:

1 is true
Hello World is true
 is true
[2, null, foo] is true
null is false
false is false

但我不确定这是否就足以证明它更 "节懒"。

2019-10-27 11:30:40
用户10536125
用户10536125

为了更方便,你可以创建一个高阶 luaIf 函数:

sealed class LuaIf<T> {
    class True<T>(val value: T) : LuaIf<T>()
    object False : LuaIf<Nothing>()
}

inline fun <T> luaIf(condition: Any?, block: () -> T): LuaIf<T> =
    if (condition == null || condition == false) False as LuaIf<T>
    else True(block())

inline infix fun <T : R, R> LuaIf<T>.els(ifFalse: () -> R): R = when (this) {
    is True -> value
    False -> ifFalse()
}

并且可以像这样使用它:

luaIf(null) { 1 } els { 0.5 } // 0.5
luaIf(Any()) { 1 } els { 0.5 } // 1

不幸的是,每当条件为 true(或不为 null)时,就会创建一个 LuaIf.True 实例。你可以通过内联 LuaIf 类来进行优化:

inline class LuaIf<T> @Deprecated(
    message = "Not type-safe, use factory method",
    replaceWith = ReplaceWith("luaIf(true) { _value }", "package.file.luaIf")
) constructor(val _value: Any?)

object LuaIfFalse

inline fun <T> luaIf(condition: Any?, ifTrue: () -> T): LuaIf<T> =
    if (condition == null || condition == false) LuaIf(LuaIfFalse)
    else LuaIf(ifTrue())

inline infix fun <T : R, R> LuaIf<T>.els(ifFalse: () -> R): R =
    if (_value == LuaIfFalse) ifFalse()
    else _value as T

现在,如果你查看 println(luaIf(nullableAny) { 1 } els { 0.5 }) 的字节码,你将看不到任何额外的对象被创建。下面是我反编译的字节码:

Object $this$els$iv = LuaIf.constructor-impl(
        nullableAny != null && !Intrinsics.areEqual(nullableAny, false) ? 1 : LuaIfFalse.INSTANCE
);
System.out.println(
        Intrinsics.areEqual($this$els$iv, LuaIfFalse.INSTANCE) ? 0.5 : $this$els$iv
);
public final class LuaIf {
   public static Object constructor-impl(@Nullable Object _value) {
      return _value;
   }
}

但由于内联类是实验性的,如果你以奇怪的方式使用它们,它们可能会导致一些错误。例如,以下代码将引发 ClassCastException: Integer cannot be cast to LuaIf

falseLua { luaIf(0) { 0 } } els { 0 }

fun <T> falseLua(other: () -> T): T = luaIf(false) { error("Impossible") } els other

我已经在 Stack Overflow 上询问了这个异常。

2019-10-27 12:10:51