Kotlin 中奇怪的值比较问题,“===” returns true 但“==” returns false
Strange value comparison issue in Kotlin, "===" returns true but "==" returns false
我在Kotlin中遇到了一个很奇怪的值比较问题,我无法解释,下面的代码打印false
data class Foo (
val a: Byte
)
fun main() {
val NUM: Byte = 1
var m: Foo? = Foo(NUM)
println(m?.a == NUM)
}
但是如果我把最后一行改成
println(m?.a === NUM)
或
println(m!!.a == NUM)
打印出来的是true,我很困惑,谁能帮忙解释一下?谢谢。
该问题只出现在1.5.20版本,1.5.10不受影响。
这似乎是较新的 kotlin 编译器版本中的一个问题。
通过一些字节码,我们可以解释问题(data class
被称为 Blah
,func
被称为 blah
)。
这是用 1.5.10 编译的字节码,returns True
for println(m?.a == NUM)
- 一切似乎都很好。我们正在做一个不等于两个数字的原语,returns False
(正确,因为 1 != 1
是 False
)。
Compiled from "WtfTest.kt"
public final class de.sfxr.WtfTest {
public de.sfxr.WtfTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public final void blah();
Code:
0: iconst_1
1: istore_1
2: new #14 // class de/sfxr/Blah
5: dup
6: iload_1
7: invokespecial #17 // Method de/sfxr/Blah."<init>":(B)V
10: astore_2
11: aload_2
12: astore_3
13: aload_3
14: invokevirtual #21 // Method de/sfxr/Blah.getA:()B
17: iload_1
18: istore_3
19: iload_3
// PRIMITIVE NOT EQUALS => False
20: if_icmpne 27
23: iconst_1
24: goto 28
27: iconst_0
28: istore_3
29: iconst_0
30: istore 4
32: getstatic #27 // Field java/lang/System.out:Ljava/io/PrintStream;
35: iload_3
36: invokevirtual #33 // Method java/io/PrintStream.println:(Z)V
39: return
}
但是在版本 1.5.20 中,字节码指示使用 JVM Intrinsics.areEqual
在内容为 1
的盒装 Integer
和盒装 Byte
上进行对象比较内容 1
,将 return False
,因为它在 Byte
上使用 equals
。这就是这个问题的原因。编译器开发人员此时肯定想要 True
。
但是为什么这个计算结果为 false?这是 Byte.equals
描述的一个片段“当且仅当参数不为 null 并且是包含与此相同的字节值的字节对象 时,结果才为真对象。"
...以及用于解释的字节码:
Compiled from "WtfTest.kt"
public final class de.sfxr.WtfTest {
public de.sfxr.WtfTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public final void blah();
Code:
0: iconst_1
1: istore_1
2: new #14 // class de/sfxr/Blah
5: dup
6: iload_1
7: invokespecial #17 // Method de/sfxr/Blah."<init>":(B)V
10: astore_2
11: aload_2
12: astore_3
13: aload_3
14: invokevirtual #21 // Method de/sfxr/Blah.getA:()B
17: invokestatic #27 // Method java/lang/Byte.valueOf:(B)Ljava/lang/Byte;
20: iload_1
21: invokestatic #32 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
// OBJECT COMPARISON VIA JVM ON BOXED BYTE(1) AND BOXED INT(1) => False
24: invokestatic #38 // Method kotlin/jvm/internal/Intrinsics.areEqual:(Ljava/lang/Object;Ljava/lang/Object;)Z
27: istore_3
28: iconst_0
29: istore 4
31: getstatic #44 // Field java/lang/System.out:Ljava/io/PrintStream;
34: iload_3
35: invokevirtual #50 // Method java/io/PrintStream.println:(Z)V
38: return
}
更新
来自 jetbrains 的人评论了已发出的票证 https://youtrack.jetbrains.com/issue/KT-47717 “这绝对是一个错误。”和优先专业。
我在Kotlin中遇到了一个很奇怪的值比较问题,我无法解释,下面的代码打印false
data class Foo (
val a: Byte
)
fun main() {
val NUM: Byte = 1
var m: Foo? = Foo(NUM)
println(m?.a == NUM)
}
但是如果我把最后一行改成
println(m?.a === NUM)
或
println(m!!.a == NUM)
打印出来的是true,我很困惑,谁能帮忙解释一下?谢谢。
该问题只出现在1.5.20版本,1.5.10不受影响。
这似乎是较新的 kotlin 编译器版本中的一个问题。
通过一些字节码,我们可以解释问题(data class
被称为 Blah
,func
被称为 blah
)。
这是用 1.5.10 编译的字节码,returns True
for println(m?.a == NUM)
- 一切似乎都很好。我们正在做一个不等于两个数字的原语,returns False
(正确,因为 1 != 1
是 False
)。
Compiled from "WtfTest.kt"
public final class de.sfxr.WtfTest {
public de.sfxr.WtfTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public final void blah();
Code:
0: iconst_1
1: istore_1
2: new #14 // class de/sfxr/Blah
5: dup
6: iload_1
7: invokespecial #17 // Method de/sfxr/Blah."<init>":(B)V
10: astore_2
11: aload_2
12: astore_3
13: aload_3
14: invokevirtual #21 // Method de/sfxr/Blah.getA:()B
17: iload_1
18: istore_3
19: iload_3
// PRIMITIVE NOT EQUALS => False
20: if_icmpne 27
23: iconst_1
24: goto 28
27: iconst_0
28: istore_3
29: iconst_0
30: istore 4
32: getstatic #27 // Field java/lang/System.out:Ljava/io/PrintStream;
35: iload_3
36: invokevirtual #33 // Method java/io/PrintStream.println:(Z)V
39: return
}
但是在版本 1.5.20 中,字节码指示使用 JVM Intrinsics.areEqual
在内容为 1
的盒装 Integer
和盒装 Byte
上进行对象比较内容 1
,将 return False
,因为它在 Byte
上使用 equals
。这就是这个问题的原因。编译器开发人员此时肯定想要 True
。
但是为什么这个计算结果为 false?这是 Byte.equals
描述的一个片段“当且仅当参数不为 null 并且是包含与此相同的字节值的字节对象 时,结果才为真对象。"
...以及用于解释的字节码:
Compiled from "WtfTest.kt"
public final class de.sfxr.WtfTest {
public de.sfxr.WtfTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public final void blah();
Code:
0: iconst_1
1: istore_1
2: new #14 // class de/sfxr/Blah
5: dup
6: iload_1
7: invokespecial #17 // Method de/sfxr/Blah."<init>":(B)V
10: astore_2
11: aload_2
12: astore_3
13: aload_3
14: invokevirtual #21 // Method de/sfxr/Blah.getA:()B
17: invokestatic #27 // Method java/lang/Byte.valueOf:(B)Ljava/lang/Byte;
20: iload_1
21: invokestatic #32 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
// OBJECT COMPARISON VIA JVM ON BOXED BYTE(1) AND BOXED INT(1) => False
24: invokestatic #38 // Method kotlin/jvm/internal/Intrinsics.areEqual:(Ljava/lang/Object;Ljava/lang/Object;)Z
27: istore_3
28: iconst_0
29: istore 4
31: getstatic #44 // Field java/lang/System.out:Ljava/io/PrintStream;
34: iload_3
35: invokevirtual #50 // Method java/io/PrintStream.println:(Z)V
38: return
}
更新
来自 jetbrains 的人评论了已发出的票证 https://youtrack.jetbrains.com/issue/KT-47717 “这绝对是一个错误。”和优先专业。