首页 > 解决方案 > Kotlin 中的奇怪值比较问题,“===”返回 true,但“==”返回 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)

它打印出来了我很困惑,任何人都可以帮忙解释一下吗?谢谢。

标签: androidkotlinequalsequality

解决方案


该问题仅出现在 1.5.20 版本中,而 1.5.10 版本不受影响。

这似乎是较新的 kotlin 编译器版本中的一个问题。

使用一些字节码,我们可以解释问题(data class被调用Blahfunc被调用blah)。

这是用 1.5.10 编译的字节码,它返回True-println(m?.a == NUM)一切似乎都很好。我们正在做一个原始不等于两个数字,它返回False(正确,因为1 != 1is 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 版本中,字节码指示使用 JVMIntrinsics.areEqual在 boxed Integerwith content1和 boxed Bytewith content上进行对象比较1,这将返回False,因为它使用equalson Byte。这是这个问题的根本原因。编译器开发人员此时肯定想要一个True

但是为什么这评估为假?这是 's 描述的片段Byte.equals“当且仅当参数不为 null并且是包含与此对象相同的字节值的 Byte 对象时,结果才为真。”

...以及解释的字节码:

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 。和优先专业。

所以恭喜,你找到了一些东西:-)


推荐阅读