skip to content

Kotlin <-> Java Type Mapping

Kotlin's Int becomes a JVM int or Integer depending on position, String is java.lang.String, Any is Object, and the collection interfaces map onto java.util types. Being able to state when an Int boxes is the practically useful part.

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

questions

5

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

open as a page

How do Kotlin's read-only collection interfaces (List, Map, Set) map onto java.util types, and what does 'read-only' actually guarantee at runtime?

level: middleimportance: must knowfreq 65%

basics

~10 s

Kotlin's List/Set/Map and their MutableList/MutableSet/MutableMap versions all map to the same java.util.List/Set/Map at runtime. 'Read-only' is just a compile-time view: it hides the mutating methods but doesn't make the underlying object immutable.

open as a page

How do kotlin.String and kotlin.Any map onto Java types, and what is surprising about the String mapping given Kotlin's null-safety?

level: middleimportance: must knowfreq 60%

basics

~20 s

kotlin.String is literally java.lang.String at runtime — same class, same methods. kotlin.Any maps to java.lang.Object. The surprise is that the same Java String class can appear in Kotlin as either non-null String or nullable String?, because Java doesn't track nullability.

open as a page

Explain how Array<Int>, IntArray, and List<Int> each map to Java types, and why Kotlin provides separate specialized array types.

level: seniorimportance: should knowfreq 35%

basics

~10 s

Array<Int> becomes Integer[] (boxed objects), IntArray becomes the primitive int[], and List<Int> becomes a java.util.List of Integer. Kotlin has specialized arrays like IntArray so numeric data can stay as fast primitive arrays without boxing.

open as a page

You expose a Kotlin API returning List<User> to Java consumers and want them to NOT mutate it. Given how Kotlin collections map to java.util, why is the bare return type insufficient, and what would you do?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Returning Kotlin's read-only List doesn't stop Java callers from mutating it, because at runtime it's just a java.util.List with add/remove. To actually prevent mutation, return an unmodifiable wrapper or a truly immutable collection, and consider a defensive copy.

open as a page