Boxing & Equality
Kotlin shows a single Int type, but under the hood on the JVM it lives in two forms: the primitive int (held by value, no allocation) and the wrapper object Integer (held on the heap, reached by reference). The move from the first form to the second is called boxing. It happens silently — as soon as a number lands where an object is required: in Int?, in a generic, in a collection.
The main trap surfaces at comparison. The === operator compares references, == compares content. For boxed numbers === depends on whether it is the same object, and the JVM caches small Integers, so === yields true for 1 and false for 1000. Understanding these transitions separates "learned the syntax" from "understand the runtime". The full map is in the layers below.
Topic map
- Boxing of primitives — when
Intcompiles to the primitiveintand when to theIntegerobject. - Nullable types and boxing — why
Int?is always boxed and how it differs fromInt. - Reference vs structural equality — the difference between
===and==, and when boxing makes===treacherous. - The Integer cache — why
Integerfor-128..127is cached and how that changes the===result.
Common traps
| Mistake | Consequence |
|---|---|
Believing an Int in Kotlin is always an object | In fact a non-nullable Int is the primitive int, with no allocation |
Thinking Int? and Int are the same at runtime | Int? is boxed to Integer, Int is a primitive; different representation |
Comparing object numbers with === | Comparison by reference — true/false depends on the cache, not the value |
Generalizing the === result from small numbers to large ones | The cache covers only -128..127; outside it two objects are not ===-equal |
Forgetting that a collection or generic boxes an Int | Hidden Integer allocations in hot code |
Interview relevance
Boxing is asked as a check of whether you see the real JVM representation behind Kotlin's clean syntax. A candidate who says "Int is a primitive, Int? is a boxed Integer, and === compares references" immediately gets ahead of one who is sure "everything is an object in Kotlin".
Typical checks:
- When
Intstays a primitive and when it is boxed. - How
Int?differs fromIntat the bytecode level. - The difference between
===(reference) and==(content). - Why
===yieldstruefor1butfalsefor1000.
Common wrong answer: "there are no primitives in Kotlin, every Int is an object". In fact the compiler represents a non-nullable Int as the primitive int; it becomes an object only when boxed — in Int?, a generic, or a collection.