'Strange value comparison issue in Kotlin, "===" returns true but "==" returns false
I have encountered a very strange value comparison issue in Kotlin that I cannot explain, the following code prints false
data class Foo (
val a: Byte
)
fun main() {
val NUM: Byte = 1
var m: Foo? = Foo(NUM)
println(m?.a == NUM)
}
But if I change the last line to
println(m?.a === NUM)
or
println(m!!.a == NUM)
it prints true, I'm so confused, anyone could help to explain? Thanks.
Solution 1:[1]
The issue only appears in version 1.5.20, while 1.5.10 is not affected.
This seems to be an issue in the newer kotlin compiler version.
With some bytecode, we can explain the problem (data class
was called Blah
, func
was called blah
).
This is the bytecode, compiled with 1.5.10, that returns True
for println(m?.a == NUM)
- everything seems to be fine. We're doing a primitive not equals of the two numbers, which returns False
(correct since 1 != 1
is 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
}
However in Version 1.5.20, the bytecode instructs for an object comparison using the JVMs Intrinsics.areEqual
on a boxed Integer
with content 1
and a boxed Byte
with content 1
, which will return False
, since it uses equals
on Byte
. This is the cause of this issue. The compiler devs surely wanted a True
at this point.
But why does this evaluate to false? Here's a snippet of the Byte.equals
's description "The result is true if and only if the argument is not null and is a Byte object that contains the same byte value as this object."
...and the bytecode for explaination:
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
}
UPDATE
The guys from jetbrains commented the issued ticket https://youtrack.jetbrains.com/issue/KT-47717 with "That's definitely a bug." and priority major.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |