Kotlin: равное сравнение кажется нормальным для nullable, но больше, чем сравнение не - PullRequest
1 голос
/ 28 марта 2019

Я новичок в Котлине.Я следую учебному пособию, в котором часть GUI включает этот фрагмент кода:

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

mouseEvent - это , очевидно, что-то обнуляемое .В предыдущем опыте кодирования я привык менять строку вроде mouseEvent?.clickCount == 2 на mouseEvent?.clickCount > 1 (или, может быть, >=2), , чтобы убедиться, что нет углового случая , где щелчки происходят так быстро, что этоскачки с 1 до 3 или что-то подобное.


Итак, я переключил этот код на:

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

После переключения ( изменив ==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'.

Это вызвало у меня 2 вопроса:

  1. Почему ==2 работает нормально, но >=2 не работает?(Я попробовал >1, который выдал ту же ошибку, что и >=2.)
  2. Как правильно обрабатывать угловой случай, где то, что я действительно хочу, - это что-то большее, чем 1?

Мне нравится идея о том, чтобы null s не мешал во время выполнения, но я бы хотел, чтобы Kotlin просто полностью избавился от значений null и сделал что-то вроде Rust или Haskell.(Мне действительно нравится то, что я видел о Kotlin до сих пор.)

Ответы [ 2 ]

3 голосов
/ 28 марта 2019

Как вы обнаружили, операторы равенства Котлина (== и !=) могут обрабатывать нули, а операторы сравнения заказов (<, <=, >, >=) не могут .

Это, вероятно, потому, что очевидно, что проверки на равенство должны означать для нулей - два нуля явно равны, а ненулевое значение никогда не должно равняться нулю - хотя не совсем понятно, что должно значит для сравнения заказов. (Если ноль не <0, значит ли это, что ноль> = 0? Если нет, у вас больше нет четко определенного порядка.)

Это отражено в реализации: Any имеет метод equals(), указывающий, что все объекты могут быть проверены на равенство. ( документация от Kotlin не делает этого явным, но для базового метода Java говорит , что ненулевые объекты никогда не должны равняться нулю.) И реализация Kotlin == и != операторы явно проверяют наличие нулей. (a == b переводится на то, что вам нужно прописать в Java: a == null ? b == null : a.equals(b).)

Но сравнение заказов обрабатывается по-другому. Он использует интерфейс Comparable: это реализуют только типы с «естественным упорядочением»; те, которые не, не могут быть сопоставлены таким образом. Поскольку null не может реализовывать какие-либо интерфейсы, он не может иметь естественного упорядочения, и компилятор не позволяет вам выполнить сравнение. (Опять же, документация от Kotlin не делает это явным, поскольку параметр не имеет значения NULL; но в документации для базового интерфейса Java говорится , что такое сравнение должно возвращать исключение NullPointerException. )

Что касается того, как вы должны справиться с этим, оператор Элвиса, вероятно, является наиболее кратким решением:

if (mouseEvent?.clickCount ?: 0 >= 2)

Если mouseEvent не равно нулю, то получится clickCount; в противном случае безопасный вызов ?. выдаст ноль напрямую, а затем ?: заменит 0. (Это также произойдет, если clickCount будет содержать ноль, хотя это не должно быть возможно.) В любом случае , в результате вы получите целое число, не равное нулю, которое можно безопасно сравнить с 2.

Конечно, на практике ничто не должно вызывать метод слушателя и передавать нулевое событие. (Я не могу вспомнить, чтобы когда-либо позволял это возвращать, когда я писал для написания кода на Java Swing, или когда возникали какие-либо проблемы в результате.) Таким образом, более простой альтернативой может быть объявление параметра как обнуляемого. Но правильная обработка нуля просто немного безопаснее; и в этом случае он не добавляет много дополнительного кода. Так что решать вам!

1 голос
/ 28 марта 2019

Какое поведение от mouseEvent?.clickCount >= 2 вы ожидаете, когда mouseEvent == null?

Вы можете преобразовать mouseEvent?.clickCount в NotNull, используя оператор elvis (?:):

val clickCount = mouseEvent?.clickCount ?: 0
if (clickCount >= 2) {
    launchSelectedSample()
}

В этом случае mouseEvent?.clickCount будет 0, если mouseEvent равно нулю

==2 работает, потому что mouseEvent?.clickCount рассматривается Котлиным как null, а сравнение null == 2 корректно в отличие от null >= 2

...