ПрограммированиеBackend разработчик

Объясните работу операторов '==' и '===' в Kotlin. В чем специфика сравнения объектов и значимых типов, что под капотом происходит при сравнении, и как избежать распространённой ошибки с эквивалентностью?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

В Kotlin есть два оператора для сравнения:

  • ==структурное сравнение (equivalent to .equals()). Оно проверяет содержания: a == b вызывает a?.equals(b) ?: (b == null).
  • ===ссылочное сравнение. Проверяет, указывают ли обе переменные на один и тот же объект: a === b эквивалентно Java a == b для объектов.

Для примитивных типов (например, Int): == и === могут вести себя одинаково из-за автоупаковки, но в общем случае нужно использовать ==.

Пример кода

val a = "Kotlin" val b = "Kotlin" val c = a println(a == b) // true (сравнение содержимого) println(a === b) // false (разные ссылки — строки интернированы зависит от компилятора) println(a === c) // true (один и тот же объект)

Вопрос с подвохом.

В чём отличие между a.equals(b) и a == b в Kotlin?

Некоторые уверяют, что разницы нет, однако если a — nullable, то вызов a.equals(b) вызовет NullPointerException, а a == b безопасен и вернёт true, если оба null, или false, если только один из них null.

Пример:

val a: String? = null val b: String? = null println(a == b) // true println(a?.equals(b)) // null (а не true, но не авария) println(a.equals(b)) // NullPointerException

Примеры реальных ошибок из-за незнания тонкостей темы.


История

В крупном проекте активно сравнивали nullable строки через a.equals(b), не подозревая, что при a == null приложение падает. Баг повторялся довольно редко, но приводил к фатальным авариям на продакшене — исправили заменой на a == b.


История

При сравнении объектов через === ожидалось сравнение содержимого, но === проверяет идентичность объектов — оказалось, что две разные строки с одинаковым содержимым не равны через ===, что сломало логику кэширования данных.


История

Работая с boxed Int-значениями (например, из коллекций), разработчики сравнивали их через === и получали неожиданные результаты из-за того, что объектные инстансы разных чисел не обязательно интернируются как примитивы. Это приводило к некорректному поведению при работе с коллекциями объектов.