skip to content

When you write a Kotlin function that takes a kotlin.Int parameter, what Java type does it become in the compiled bytecode, and when would it instead become java.lang.Integer?

level: juniorimportance: must knowfreq 70%

answer

  1. Int = mapped type, no kotlin/Int class at runtime
  2. Non-null/non-generic → primitive int
  3. Nullable Int? → boxed Integer (primitive can't be null)
  4. Generic List<Int> → List<Integer> (erasure is reference-only)
  5. Same rule for Long/Double/Boolean/Char

basics

~20 s

Kotlin's Int usually compiles to Java's primitive int, which is fast and uses no extra memory. But when the value can be null or is used as a generic type argument, Kotlin boxes it into Integer instead.

solid answer

~30 s

kotlin.Int is a 'mapped type': there is no separate Int class at runtime. The compiler emits the JVM primitive int wherever it can — plain parameters, locals, return types — for performance and zero boxing. It switches to the boxed java.lang.Integer when the value must be a reference: nullable Int? (a primitive can't be null), generic type arguments like List<Int> (JVM generics are reference-only, so it becomes List<Integer>), and platform spots that need Object. So `fun f(x: Int)` → `void f(int x)`, but `fun f(x: Int?)` → `void f(Integer x)`. Other primitives map the same way: Long→long/Long, Double→double/Double, Boolean→boolean/Boolean, Char→char/Character.

code

kotlin · 4 lines
kotlin
fun sum(a: Int, b: Int): Int = a + b   // int sum(int, int)
fun maybe(x: Int?): Int? = x           // Integer maybe(Integer)
val primitives = intArrayOf(1, 2, 3)    // int[]
val boxed: List<Int> = listOf(1, 2, 3)  // List<Integer>

go deeper

for a junior

Knows Int usually becomes primitive int and is fast; can state that null forces boxing.

for a middle

Explains the non-null→primitive vs nullable/generic→Integer rule precisely with examples.

for a senior

Connects boxing to generic erasure, IntArray vs List<Int>, and the performance implications in hot loops.

for a principal

Reasons about API design across the boundary — choosing IntArray vs List<Int>, allocation costs, and how nullability of numeric fields ripples into boxing for Java consumers.

## What 'mapped type' means Some Kotlin types do not exist as their own classes at runtime. Instead the Kotlin compiler **maps** them onto existing JVM types when it emits bytecode. `kotlin.Int` is one of these: you never get a `kotlin/Int` class file — you get the JVM's `int` or `java.lang.Integer`. ## Primitive vs boxed The JVM has two worlds: - **Primitives** (`int`, `long`, `double`, `boolean`, `char`, …): raw values, fast, cannot be null, cannot be used as generic type arguments. - **Boxed reference types** (`java.lang.Integer`, `Long`, …): objects on the heap, can be null, can be a generic argument, cost an allocation. Kotlin hides this split behind the single type `Int`, then picks the right JVM form per usage. ## When Int → primitive `int` - Non-null parameters: `fun f(x: Int)` → `void f(int)` - Non-null return: `fun g(): Int` → `int g()` - Local variables and arithmetic ## When Int → boxed `java.lang.Integer` - **Nullable**: `Int?` must be boxed because a primitive can't hold null. - **Generic type arguments**: `List<Int>` becomes `List<Integer>` — the JVM erases generics to reference types, so primitives are not allowed there. - Anywhere an `Any`/`Object` reference is needed. ```kotlin fun a(x: Int) {} // void a(int x) fun b(x: Int?) {} // void b(Integer x) fun c(): Int = 1 // int c() val xs: List<Int> = listOf(1) // List<Integer> val n: Int? = null // Integer (null) ``` ## Why it matters - **Performance**: tight numeric loops stay on primitives, avoiding allocation and GC pressure. - **Interop**: a Java caller sees `int` for `Int` and `Integer` for `Int?`/generic uses — exactly the Java types they expect. ## Related mapped numeric types `Long`↔`long`/`Long`, `Short`↔`short`/`Short`, `Byte`↔`byte`/`Byte`, `Double`↔`double`/`Double`, `Float`↔`float`/`Float`, `Boolean`↔`boolean`/`Boolean`, `Char`↔`char`/`Character`. Each is primitive when non-null/non-generic, boxed otherwise.

  • Why can't List<Int> use the primitive int on the JVM?
    JVM generics are erased to reference types; type arguments must be objects, so Int is boxed to Integer. Only specialized array types like IntArray map to primitive int[].
  • What is the difference between List<Int> and IntArray at the bytecode level?
    List<Int> is List<Integer> (boxed objects). IntArray maps to the JVM primitive array int[], so no boxing occurs — that's why IntArray exists as a separate type.

Int is one Kotlin label on two JVM boxes — a lightweight primitive box for everyday values, and a heavier object box when something needs to be nullable or stored in a generic collection.

saying these in an interview costs you the question

  • Claiming Int is always boxed to Integer
  • Saying there is a kotlin.Int class loaded at runtime
  • Believing List<Int> stores primitives
  • Thinking Int? compiles to a primitive int
  • Confusing IntArray with Array<Int> (the latter is Integer[])

context