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.
Equality In Kotlin there are two types of equality: Structural equality ( == - a check for equals() ) Referential equality ( === - two references point to the same object)
Structural Equality ('==') == operator in Kotlin only compares the data or variables, whereas in Java or other languages == is generally used to compare the references. The negated counterpart of == in Kotlin is != which is used to compare if both the values are not equal to each other.
In Kotlin, == is the default way to compare two objects: it compares their values by calling equals under the hood. Thus, if equals is overridden in your class, you can safely compare its instances using ==. For reference comparison, you can use the === operator, which works exactly the same as == in Java.
:: converts a Kotlin function into a lambda. this translates to MyClass(x, y) in Kotlin.
Another method to compare two strings in Kotlin is to use the equals () function. This comparison is case sensitive. For case-insensitive string comparison in Kotlin, pass the second argument as True. 3. Using CompareTo () function Kotlin has another method, CompareTo (), which is used to check the order of two strings.
The negated counterpart of ==in Kotlin is !=which is used to compare if both the values are not equal to each other. Referential equality (‘===’) ===operator is used to compare the reference of two variable or object. It will only be trueif both the objects or variables pointing to the same object.
In Kotlin, == is used to check the structural equality of two objects. It will return true if both the objects have the same value: Koltin uses the === operator for referential equality. It returns true if the two variables are pointing to the same object and have the same value.
The Boolean value of an expression is the basis for all Kotlin comparisons and conditions. You will learn more about conditions in the next chapter.
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
}
The guys from jetbrains commented the issued ticket https://youtrack.jetbrains.com/issue/KT-47717 with "That's definitely a bug." and priority major.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With