elvis表达式踩坑经验

74 阅读2分钟

Intro

kotlin中判空可以使用"?"直接处理

// java
void test(String a) {
  if (a != null) {
    print(a);
  }
}


// kotlin
fun test(a: String?) = a?.let {
    println(a)
}

那么else的逻辑如何处理呢

这就要引入今天的主角 elvis表达式,即"?:"的用法

// java
void test(String a) {
  if (a != null) {
    print(a);
  } else {
    print("null");
  }
}


// kotlin
fun test(a: String?) = a?.let {
    println(a)
} ?: let {
    print("null")
}

这时候编译器就会提醒你上述方法可以简化

image.png

变成下面这种形式

image.png

但是这个形式不规整,于是“聪明的同学”就会增加大括号

fun test3(a: String?) = a?.let {
    println(a)
} ?: {
    println("canput")
}

但是这2个逻辑真的等价吗

实际运行时我们会发现并不等价,因为在上述函数中,当传入null时 并不能打印canput

Derivation

那么问题出在哪里呢,为了探究这个问题我们请出看源码的神器 show kotlin bytecode

   fun test(a: String?) = a?.let {
        println(a)
    } ?: println("canput")

    fun test1(a: String?) = a?.let {
        println(a)
    } ?: let {
        println("canput")
    }

    fun test2(a: String?) = a?.let {
        println(a)
    } ?: run {
        println("canput")
    }

    fun test3(a: String?) = a?.let {
        println(a)
    } ?: {
        println("canput")
    }
   public final void test(@Nullable String a) {
      if (a != null) {
         int var5 = 0;
         System.out.println(a);
         Unit var2 = Unit.INSTANCE;
      } else {
         String var3 = "canput";
         System.out.println(var3);
      }

   }

   public final void test1(@Nullable String a) {
      if (a != null) {
         int var5 = 0;
         System.out.println(a);
         Unit var2 = Unit.INSTANCE;
      } else {
         newtest it = this;
         int var7 = 0;
         String var6 = "canput";
         System.out.println(var6);
      }

   }

   public final void test2(@Nullable String a) {
      if (a != null) {
         int var5 = 0;
         System.out.println(a);
         Unit var2 = Unit.INSTANCE;
      } else {
         newtest $this$test2_u24lambda_u244 = this;
         int var7 = 0;
         String var6 = "canput";
         System.out.println(var6);
      }

   }

   @NotNull
   public final Object test3(@Nullable String a) {
      Object var10000;
      if (a != null) {
         int var5 = 0;
         System.out.println(a);
         Unit var2 = Unit.INSTANCE;
         var10000 = var2;
      } else {
         var10000 = newtest::test3$lambda$6;
      }

      return var10000;
   }

   private static final Unit test3$lambda$6() {
      String var0 = "canput";
      System.out.println(var0);
      return Unit.INSTANCE;
   }

可以很明显看到前3个方法都可以执行,但是test3中的逻辑被封装成了一个静态方法作为返回值return 实际里面的逻辑不会执行

Conclusion

?: 等效于 else { }

如下所示 只有加上run/let等关键字 才可以执行里面的方法

fun test3(a: String?) = a?.let {
    println(a)
} ?: {
    println("canput")
}

fun test3(a: String?) {
    if (a != null) {
        println(a)
    } else {
        {
            println("canput")
        }
    }
}