skip to content

== vs ===

== is structural equality that calls equals() and handles nulls for you, while === compares references. Interviewers push on the edge cases where == and equals() diverge for Double, around NaN and negative zero.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

In Kotlin, what is the difference between == and ===? Which one calls equals()?

level: juniorimportance: must knowfreq 85%

answer

  1. == is structural -> calls equals()
  2. === is referential -> same instance
  3. == compiles to a?.equals(b) ?: (b === null), null-safe
  4. != and !== negate them
  5. 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 s

In 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 lines
kotlin
val 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

for a junior

States == is structural (calls equals) and === is referential (same object); picks == for value comparison.

for a middle

Recalls the desugaring a?.equals(b) ?: (b === null) and explains null-safety; mentions data class equals generation.

for a senior

Discusses when identity (===) is legitimately needed and string interning caveats; ties to hashCode contract.

for a principal

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

context

open as a page

If you override equals() for == to behave correctly in a HashSet/HashMap, what else must you do and why?

level: middleimportance: must knowfreq 55%

basics

~10 s

You must also override hashCode() so equal objects have the same hash. Hash-based collections use hashCode() to find the bucket, then equals() to confirm. Override one without the other and lookups break.

open as a page

How does the Kotlin compiler translate a == b, and why does that make == null-safe?

level: middleimportance: must knowfreq 70%

basics

~20 s

The compiler turns a == b into a null-check plus equals(). If a is null it doesn't call equals(); it just checks whether b is also null. So == never crashes on a null value.

open as a page

How do == and equals() differ when comparing Double values, specifically for NaN and -0.0?

level: seniorimportance: should knowfreq 45%

basics

~10 s

For Doubles used directly, == follows IEEE 754: NaN == NaN is false and -0.0 == 0.0 is true. But equals() (and boxed/generic comparisons) flips this: NaN.equals(NaN) is true and (-0.0).equals(0.0) is false.

open as a page

When is === (referential equality) the right tool, and what are the risks of relying on it — for example with boxed Int caching?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

Use === when you truly care about object identity: checking something is the same instance, like a cache hit or breaking a reference cycle. Avoid it for value comparison because identical-looking values may or may not be the same object.

open as a page