ProgrammingBackend Developer

Explain the work of the operators '==' and '===' in Kotlin. What is the specificity of comparing objects and value types, what happens under the hood during comparison, and how to avoid the common mistake with equivalence?

Pass interviews with Hintsage AI assistant

Answer.

In Kotlin, there are two operators for comparison:

  • ==structural comparison (equivalent to .equals()). It checks the contents: a == b calls a?.equals(b) ?: (b == null).
  • ===referential comparison. It checks whether both variables point to the same object: a === b is equivalent to Java a == b for objects.

For primitive types (e.g., Int): == and === may behave the same due to auto-boxing, but in general, == should be used.

Code Example

val a = "Kotlin" val b = "Kotlin" val c = a println(a == b) // true (content comparison) println(a === b) // false (different references — string interning depends on the compiler) println(a === c) // true (the same object)

Trick Question.

What is the difference between a.equals(b) and a == b in Kotlin?

Some believe there is no difference, however, if a is nullable, calling a.equals(b) will throw a NullPointerException, while a == b is safe and will return true if both are null or false if only one of them is null.

Example:

val a: String? = null val b: String? = null println(a == b) // true println(a?.equals(b)) // null (not true, but not a crash) println(a.equals(b)) // NullPointerException

Examples of real errors due to lack of understanding of the nuances of the topic.


Story

In a large project, nullable strings were actively compared using a.equals(b), unaware that if a == null, the application crashes. The bug occurred quite rarely but led to fatal crashes in production — it was fixed by replacing it with a == b.


Story

When comparing objects using ===, the expectation was for content comparison, but === checks object identity — it turned out that two different strings with the same content are not equal via ===, which broke the logic of data caching.


Story

When working with boxed Int values (e.g., from collections), developers compared them using === and received unexpected results because object instances of different numbers are not necessarily interned like primitives. This led to incorrect behavior when working with object collections.