Kotlin:相等比较似乎可以为空,但大于比较不是

Mik*_*son 8 nullable kotlin

我是 Kotlin 的新手。我正在遵循一个教程,其中 GUI 部分涉及此代码片段:

    sampleList.addMouseListener(object: MouseAdapter() {
        override fun mouseClicked(mouseEvent: MouseEvent?) {
            if (mouseEvent?.clickCount == 2) {
                launchSelectedSample()
            }
        }
    })
Run Code Online (Sandbox Code Playgroud)

mouseEvent很明显的东西可空。在以前的编码经验中,我习惯于将一行更改mouseEvent?.clickCount == 2mouseEvent?.clickCount > 1(或可能为>=2),以确保不会出现点击发生如此之快以至于从 1 跳到 3 或类似情况的极端情况。


所以,我将此代码切换为:

    sampleList.addMouseListener(object: MouseAdapter() {
        override fun mouseClicked(mouseEvent: MouseEvent?) {
            if (mouseEvent?.clickCount >= 2) {
                launchSelectedSample()
            }
        }
    })
Run Code Online (Sandbox Code Playgroud)

进行切换(更改==2>=2)后,我从 IntelliJ 收到以下错误:

Operator call corresponds to a dot-qualified call 'mouseEvent?.clickCount.compareTo(2)' which is not allowed on a nullable receiver 'mouseEvent?.clickCount'.
Run Code Online (Sandbox Code Playgroud)

这给我提出了两个问题:

  1. 为什么==2工作正常,但>=2不起作用?(我试过>1,它给出了与 相同的错误>=2。)
  2. 处理极端情况的正确方法是什么,我真正想要的是大于 1 的任何值?

我喜欢确保nulls 在运行时不会搞砸的想法,但我有点希望 Kotlin 完全摆脱null值并做一些像 Rust 或 Haskell 的事情。(不过,我确实喜欢到目前为止我对 Kotlin 的看法。)

gid*_*dds 16

正如您所发现的,Kotlin 的相等运算符 ( ==and !=) 可以处理空值,而顺序比较运算符 ( <, <=, >, >=) 不能。

这可能是因为很明显,相等性检查对于空值应该意味着什么——两个空值显然相等,一个非空值永远不应该等于空值——而对于顺序比较它应该意味着什么根本不清楚。(如果 null 不是 < 0,这是否意味着 null >= 0?如果不是,您不再有明确定义的排序。)

这体现在实现上:Any有一个equals()方法,表示可以检查所有对象是否相等。(Kotlin 的文档明确指出,就像底层Java 方法一样,非空对象永远不能等于空。)而且 Kotlin 的==and!=运算符实现明确检查空值。(a == b转换为您必须用 Java 拼写的内容:a == null ? b == null : a.equals(b).)

但是顺序比较的处理方式不同。它使用Comparable接口:只有具有“自然排序”的类型才能实现;那些没有的,不能以这种方式进行比较。由于 null 不能实现任何接口,它不能具有自然排序,并且编译器会阻止您尝试进行比较。(科特林的文档没有更加明确,因为参数是不可为空;但是,对于底层的Java接口,这样的比较应该返回一个NullPointerException。)

至于你应该如何处理这个问题,Elvis 运算符可能是最简洁的解决方案:

if (mouseEvent?.clickCount ?: 0 >= 2)
Run Code Online (Sandbox Code Playgroud)

如果mouseEvent不为空,这将得到它的clickCount; 否则,安全调用?.将直接给出空值,然后?:将替换为 0。(如果clickCount持有空值也会发生这种情况,尽管这应该不可能。)在每种情况下,你最终都会得到一个不可为空的可以安全地与 2 进行比较的整数。

当然,在实践中,不应该调用侦听器方法并传递空事件。(我不记得当我曾经以编写 Java Swing 代码为生,或者因此遇到任何问题时曾经允许这样做。)因此,一个更简单的替代方法可能是将参数声明为不可为空。但是正确处理 null 只是稍微安全一点;在这种情况下,它不会添加太多额外的代码。所以这取决于你!