skip to content

Java Arrays & Varargs

Kotlin's Array<T> maps to a Java T[] and is invariant, while IntArray and friends map to primitive arrays. Passing an existing array into a Java varargs method takes the spread operator, which copies.

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

questions

5

When you call a Java method that takes an int[] from Kotlin, what Kotlin type do you pass, and why not Array<Int>?

level: juniorimportance: must knowfreq 70%

answer

  1. int[] -> IntArray, not Array<Int>
  2. Array<Int> == Integer[] (boxed)
  3. Specialized arrays = unboxed primitives
  4. intArrayOf / toIntArray / toTypedArray
  5. String[] -> Array<String>

basics

~10 s

Use Kotlin's IntArray for a Java int[]. Array<Int> is an array of boxed Integer objects (Integer[]), which is a different Java type and won't match an int[] parameter.

solid answer

~30 s

Kotlin maps Java primitive arrays to dedicated specialized classes: int[] -> IntArray, long[] -> LongArray, double[] -> DoubleArray, boolean[] -> BooleanArray, char[] -> CharArray, byte[] -> ByteArray, short[] -> ShortArray, float[] -> FloatArray. These hold unboxed primitives, so they compile down to the JVM primitive array (int[], etc.) and carry no boxing overhead. Array<Int>, by contrast, compiles to Integer[] (boxed). A Java method whose parameter is int[] therefore accepts IntArray, not Array<Int>. Build them with intArrayOf(1, 2, 3) or IntArray(size) { it }. Object/reference Java arrays (String[], Object[]) map to Array<String>, Array<Any?>, etc.

code

kotlin · 7 lines
kotlin
// Java: static int sum(int[] xs)
val xs: IntArray = intArrayOf(1, 2, 3)
val total = JavaMath.sum(xs)          // OK

val boxed: Array<Int> = arrayOf(1, 2, 3)
// JavaMath.sum(boxed)               // compile error: Integer[] != int[]
val fixed = JavaMath.sum(boxed.toIntArray())  // convert first

go deeper

for a junior

Knows to use IntArray for int[] and can build one with intArrayOf.

for a middle

Explains the boxing difference (Integer[] vs int[]) and the conversion functions toIntArray/toTypedArray.

for a senior

Articulates why generics force Array<T> to be a reference array on the JVM and the performance implications of boxing.

for a principal

Reasons about when specialized arrays matter for hot paths/memory and how API designers should expose them to keep Java callers boxing-free.

## The two kinds of arrays in Kotlin Kotlin deliberately splits arrays into two families to interoperate cleanly with the JVM: - **`Array<T>`** is a generic array of object references. Because the JVM erases generics but keeps array element types, `Array<T>` always compiles to a *reference* array. So `Array<Int>` becomes `java.lang.Integer[]` (each element is a **boxed** `Integer` object), and `Array<String>` becomes `String[]`. - **Specialized primitive arrays** (`IntArray`, `LongArray`, `DoubleArray`, `FloatArray`, `ShortArray`, `ByteArray`, `CharArray`, `BooleanArray`) compile directly to the JVM primitive arrays `int[]`, `long[]`, `double[]`, etc. They store **unboxed** primitives — no `Integer` wrappers, no per-element allocation. ## Why this matters for Java interop A Java API written as `void process(int[] data)` has a parameter of JVM type `int[]`. From Kotlin you must pass an `IntArray`, because only `IntArray` *is* an `int[]` at the bytecode level. Passing `Array<Int>` would mean handing over an `Integer[]`, which the JVM will not accept where an `int[]` is required — it is a different, incompatible type. The mismatch is caught at compile time. ## Construction ```kotlin // Java: void process(int[] data) val prim: IntArray = intArrayOf(1, 2, 3) process(prim) // OK: IntArray == int[] val sized = IntArray(5) { it * it } // [0,1,4,9,16] val boxed: Array<Int> = arrayOf(1, 2, 3) // Integer[] — does NOT fit int[] ``` ## Converting between the two - `Array<Int>.toIntArray()` — boxed to primitive. - `IntArray.toTypedArray()` — primitive to boxed `Array<Int>`. ```kotlin val p: IntArray = arrayOf(1, 2, 3).toIntArray() val b: Array<Int> = intArrayOf(1, 2, 3).toTypedArray() ``` ## Full mapping table | Java | Kotlin specialized | Kotlin generic | |------|--------------------|----------------| | `int[]` | `IntArray` | `Array<Int>` (= `Integer[]`) | | `long[]` | `LongArray` | `Array<Long>` | | `double[]` | `DoubleArray` | `Array<Double>` | | `boolean[]` | `BooleanArray` | `Array<Boolean>` | | `char[]` | `CharArray` | `Array<Char>` | | `byte[]` | `ByteArray` | `Array<Byte>` | | `String[]` | `Array<String>` | — | Rule of thumb: **primitive Java array -> specialized Kotlin array; object/reference Java array -> `Array<T>`.**

  • How do you turn an Array<Int> into something a Java int[] parameter accepts?
    Call .toIntArray() on it; that unboxes each element into a primitive int[].
  • Does IntArray have any boxing overhead when iterated?
    No — elements are primitive int, so iteration and indexing avoid Integer allocation, unlike Array<Int>.

IntArray is a crate of loose nails (primitives); Array<Int> is a crate of nails each in its own gift box (boxed Integers) — the machine that wants loose nails rejects the gift boxes.

saying these in an interview costs you the question

  • Claiming Array<Int> and IntArray are interchangeable for Java int[] parameters
  • Thinking Array<Int> compiles to int[]
  • Not knowing the specialized array classes exist
  • Confusing arrayOf with intArrayOf

context

open as a page

How do you pass an existing array into a Java (or Kotlin) vararg parameter, and what does the * operator do?

level: middleimportance: must knowfreq 65%

basics

~10 s

Use the spread operator: put * before the array, like foo(*arr). It unpacks the array so each element is passed as a separate vararg argument instead of one array argument.

open as a page

Kotlin's Array<T> is invariant. What does that mean, and how does it differ from Java arrays' covariance when crossing the interop boundary?

level: middleimportance: should knowfreq 45%

basics

~20 s

Invariant means Array<String> is NOT a subtype of Array<Any>, even though String is a subtype of Any. Java arrays are covariant (String[] is an Object[]), which can blow up at runtime; Kotlin blocks that at compile time.

open as a page

You have a Kotlin function `fun run(vararg args: String)`. A caller holds an Array<String>. Walk through the calls that compile, the ones that don't, and the allocation cost of each.

level: seniorimportance: should knowfreq 35%

basics

~10 s

run(*arr) compiles and spreads the array's elements. run(arr) does not compile because an Array<String> isn't a String. Each spread makes a defensive copy of the array, so spreading in a loop allocates repeatedly.

open as a page

When Kotlin calls a Java method `void copy(Object[] dest, int[] src)`, how does Kotlin surface those array parameter types, and what subtle gotchas (nullability, primitives, variance) should you watch for?

level: principalimportance: nice to knowfreq 20%

basics

~20 s

Kotlin sees the Java arrays as platform types with unknown nullability (shown like Array<Any!>! and IntArray!). The primitive int[] becomes IntArray; the Object[] becomes Array<(out) Any!>. You decide the nullability and must pass IntArray, not Array<Int>.

open as a page