问题引入:
编码过程:
我们写了以下代码 ,IDE 提示我们添加 == true
于是我们按提示加了 == true 有了以下代码
class TestKotlin {
var list:ArrayList<Any>? = null
}
fun main() {
var test :TestKotlin? = TestKotlin()
if(test?.list?.isEmpty() == true){
//test的list属性是空的则输出 11111111
System.out.println("11111111")
return
}else{
System.out.println("22222222")
}
//其他逻辑....
System.out.println("33333333")
}
问题现象:
输出了 2222222,33333333 ,如下图所示 ,逻辑并没有按我们预想的逻辑执行
问题分析:
猜想:
IDE提示添加 == true 是说明 test?.list?.isEmpty() 返回值是 Boolean?,而非 Boolean ,故需要添加 == true, 当返回值是 Boolean? 时, == true 条件返回了 false 导致走了下面的逻辑
验证:
字节码层面 :list 为null 时不再执行 isEmpty(),会将 null 和 true 进行对比,返回 false
@Lkotlin/Metadata;(mv={1, 5, 1}, k=2, xi=48, d1={"\u0000\u0008\n\u0000\n\u0002\u0010\u0002\n\u0000\u001a\u0006\u0010\u0000\u001a\u00020\u0001\u00a8\u0006\u0002"}, d2={"main", "", "test"})
// access flags 0x19
public final static main()V
L0 // new TestKotlin
LINENUMBER 2 L0
NEW TestKotlin
DUP
INVOKESPECIAL TestKotlin.<init> ()V
ASTORE 0
L1 // 获取 list 判断是 null 执行 L3,不是 null 执行 L2
LINENUMBER 3 L1
ALOAD 0
ASTORE 1
ALOAD 1
INVOKEVIRTUAL TestKotlin.getList ()Ljava/util/ArrayList;
ASTORE 2
ALOAD 2
IFNONNULL L2
ACONST_NULL
GOTO L3
L2 // 执行 isEmpty
FRAME APPEND [TestKotlin TestKotlin java/util/ArrayList]
ALOAD 2
INVOKEVIRTUAL java/util/ArrayList.isEmpty ()Z
INVOKESTATIC java/lang/Boolean.valueOf (Z)Ljava/lang/Boolean;
L3 // 执行 equal null == true 或者 isEmpty的返回值 == true
FRAME SAME1 java/lang/Boolean
ICONST_1
INVOKESTATIC java/lang/Boolean.valueOf (Z)Ljava/lang/Boolean;
INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z
IFEQ L4 //当前栈顶等于 0 时跳转 L4
L5 //
LINENUMBER 4 L5
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "11111111"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
GOTO L6
L4 //输出 22222222
LINENUMBER 6 L4
FRAME SAME
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "22222222"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L6 //输出 33333333
LINENUMBER 8 L6
FRAME SAME
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "33333333"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L7
LINENUMBER 9 L7
RETURN
L8
LOCALVARIABLE test LTestKotlin; L1 L8 0
MAXSTACK = 2
MAXLOCALS = 3
// access flags 0x1009
public static synthetic main([Ljava/lang/String;)V
L0
INVOKESTATIC KtTestKt.main ()V
RETURN
L1
LOCALVARIABLE args [Ljava/lang/String; L0 L1 0
MAXSTACK = 0
MAXLOCALS = 1
}
修正方案:
fun main() {
var test :TestKotlin? = TestKotlin()
if(test?.list.isNullOrEmpty()){
System.out.println("11111111")
}else{
System.out.println("22222222")
}
System.out.println("33333333")
}
输出结果:
字节码验证:
// class version 52.0 (52)
// access flags 0x31
public final class KtTestKt {
// compiled from: KtTest.kt
@Lkotlin/Metadata;(mv={1, 5, 1}, k=2, xi=48, d1={"\u0000\u0008\n\u0000\n\u0002\u0010\u0002\n\u0000\u001a\u0006\u0010\u0000\u001a\u00020\u0001\u00a8\u0006\u0002"}, d2={"main", "", "test"})
// access flags 0x19
public final static main()V
L0 //new TestKoltlin
LINENUMBER 2 L0
NEW TestKotlin
DUP
INVOKESPECIAL TestKotlin.<init> ()V
ASTORE 0
L1 //取 getList 如果是null 执行 L2,否则执行isEmpty,如果true 执行 L2 否则执行L3
LINENUMBER 3 L1
ALOAD 0
ASTORE 1
ALOAD 1
INVOKEVIRTUAL TestKotlin.getList ()Ljava/util/ArrayList;
CHECKCAST java/util/Collection
ASTORE 1
ICONST_0
ISTORE 2
ICONST_0
ISTORE 3
ALOAD 1
IFNULL L2 //list 为null 执行 L2
ALOAD 1
INVOKEINTERFACE java/util/Collection.isEmpty ()Z (itf) //执行 isEmpty
IFEQ L3 //栈顶是 0 执行 L3
L2 //
FRAME FULL [TestKotlin java/util/Collection I I] []
ICONST_1 //将1推到栈顶 执行 L4
GOTO L4
L3
FRAME SAME
ICONST_0 //将 0 推送至栈顶
L4
LINENUMBER 3 L4
FRAME SAME1 I
IFEQ L5 //如果栈顶是0 执行L5 否则执行L6
L6 // 输出 1111111111
LINENUMBER 4 L6
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "11111111"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
GOTO L7
L5 //输出 2222222
LINENUMBER 6 L5
FRAME SAME
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "22222222"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L7 //执行 33333333
LINENUMBER 8 L7
FRAME SAME
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "33333333"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L8
LINENUMBER 9 L8
RETURN
L9
LOCALVARIABLE test LTestKotlin; L1 L9 0
MAXSTACK = 2
MAXLOCALS = 4
// access flags 0x1009
public static synthetic main([Ljava/lang/String;)V
L0
INVOKESTATIC KtTestKt.main ()V
RETURN
L1
LOCALVARIABLE args [Ljava/lang/String; L0 L1 0
MAXSTACK = 0
MAXLOCALS = 1
}
可以看到不管是 null 或者 empty 都会进入 L6 进行正常逻辑
总结:
Koltin 对于IDE 的 "欺骗性" 补全 要慎重,特别是 == true 的补全,要关注下 else 的异常逻辑。