In Kotlin, what is the difference between == and ===? Which one calls equals()?
answer
- == is structural -> calls equals()
- === is referential -> same instance
- == compiles to a?.equals(b) ?: (b === null), null-safe
- != and !== negate them
- data class makes == compare contents
basics
~10 s== checks if two values are equal (same contents) by calling equals(). === checks if two variables point to the exact same object in memory. Use == for value checks.
solid answer
~40 sIn Kotlin == is structural equality: a == b compiles to a?.equals(b) ?: (b === null), so it calls equals() and is null-safe — no NullPointerException even when a is null. === is referential (identity) equality: it returns true only when both references point to the same object instance, never calling equals(). Their negations are != (structural) and !== (referential). For data classes, equals() is auto-generated from the properties in the primary constructor, so == compares contents. For most app code you want ==; reach for === only when identity itself matters (cache/interning checks, cycle detection). Unlike Java, Kotlin has no separate .equals() syntax requirement for == — the operator already routes to equals().
code
kotlin · 8 linesval a: String? = null
val b: String? = null
println(a == b) // true, null-safe, no NPE
val x = StringBuilder("hi").toString()
val y = StringBuilder("hi").toString()
println(x == y) // true (same chars)
println(x === y) // false (different instances)go deeper
States == is structural (calls equals) and === is referential (same object); picks == for value comparison.
Recalls the desugaring a?.equals(b) ?: (b === null) and explains null-safety; mentions data class equals generation.
Discusses when identity (===) is legitimately needed and string interning caveats; ties to hashCode contract.
Frames equality choice as an API design concern across the codebase and warns against === leaking implementation/identity assumptions into business logic.
## Two kinds of equality Kotlin distinguishes **structural equality** (do these represent the same value?) from **referential equality** (are these the same object in memory?). - **`==` (structural)** — the equality operator. The compiler translates `a == b` into: ```kotlin a?.equals(b) ?: (b === null) ``` So it calls the `equals()` method, and it is **null-safe**: if `a` is `null`, no method is invoked and the result is simply `b === null` (true only if `b` is also null). This is why `==` never throws a `NullPointerException` in Kotlin, unlike calling `a.equals(b)` directly in Java. - **`===` (referential / identity)** — returns `true` only when the two operands point to the **exact same object instance**. It never calls `equals()`. For primitive-backed types that map to JVM primitives (e.g. `Int`), `===` compares values directly. ## Negations - `!=` negates `==` (structural). - `!==` negates `===` (referential). ## equals() and data classes `equals()` comes from `Any`. By default (a plain class) it behaves like identity, so `==` and `===` agree. A **`data class`** auto-generates `equals()`/`hashCode()` from the properties declared in the **primary constructor**, so `==` compares contents: ```kotlin data class Point(val x: Int, val y: Int) val a = Point(1, 2) val b = Point(1, 2) println(a == b) // true -> structural, contents match println(a === b) // false -> different instances ``` ## When to use which - Use **`==`** for almost everything: comparing values, strings, list contents, etc. - Use **`===`** only when identity matters: checking interning/caching, detecting that an object reference was reused, or breaking reference cycles. ## Gotcha String literals may be interned so `===` can surprisingly return `true`, but you must never rely on that — always compare strings with `==`.
- Does a == b throw if a is null?No. == compiles to a?.equals(b) ?: (b === null), so a null left operand is handled safely and returns true only when b is also null.
- What does == do for a plain (non-data) class with no overridden equals()?It falls back to Any.equals(), which is identity-based, so == behaves like === for that class.
== asks 'are these two banknotes worth the same amount?'; === asks 'is this the very same physical banknote?'
saying these in an interview costs you the question
- Saying == compares references like Java's == on objects
- Claiming == can throw NullPointerException
- Thinking === calls equals()
- Relying on === to compare String contents
- Believing != and !== are the same operator