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?
answer
- Java arrays -> platform types with trailing !
- int[] -> IntArray!, Object[] -> Array<(out) Any!>!
- (out) honors Java covariance for passing
- Nullability unchecked -> runtime NPE risk
- Annotate Java (JSpecify) to recover null safety
basics
~20 sKotlin 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>.
solid answer
~40 sJava carries no nullability annotations by default, so Kotlin treats Java-returned/parameter types as **platform types**, written with a trailing `!` (e.g. `Array<(out) Any!>!`, `IntArray!`). For `void copy(Object[] dest, int[] src)`: `int[]` maps to the specialized `IntArray` (you must pass `IntArray`, never `Array<Int>`), and `Object[]` maps to `Array<(out) Any!>` — an out-projected platform array of platform-typed elements. The `(out)` projection means Kotlin lets you pass a more specific array (e.g. `Array<String>`) into the Java parameter, matching Java's covariant arrays, but flags writes through that view in Kotlin code. Gotchas: (1) platform types let you pass `null` or non-null without a compile error, so a Java NPE/ArrayStoreException can surface at runtime; (2) annotate the Java side (`@Nullable`/`@NotNull`, JSpecify) to get precise Kotlin types; (3) primitive vs boxed array mismatch is a frequent compile error.
code
kotlin · 6 lines// Java: void copy(Object[] dest, int[] src)
val dest = arrayOfNulls<Any>(3) // Array<Any?>
val src = intArrayOf(1, 2, 3) // IntArray (must be primitive)
JavaCopier.copy(dest, src) // OK
JavaCopier.copy(arrayOf("a","b","c"), src) // OK: (out) allows Array<String>
// JavaCopier.copy(dest, arrayOf(1,2,3)) // ERROR: Array<Int> != int[]go deeper
Knows int[] becomes IntArray and you can't pass Array<Int>.
Recognizes platform types and the trailing ! and the primitive/boxed rule.
Explains the (out) projection mirroring Java covariance and the runtime NPE / ArrayStoreException risks.
Drives a policy of annotating Java APIs (JSpecify) to eliminate platform types and reasons about variance + null-safety guarantees across a mixed Java/Kotlin codebase.
## Platform types When Kotlin consumes a Java declaration with no nullability annotations, the type is a **platform type** — Kotlin neither asserts nullable nor non-null. It is displayed (only in diagnostics/IDE, you can't write it) with a trailing `!`: `String!`, `Array<Any!>!`. You may assign it to either a nullable or non-null Kotlin type; the compiler trusts you and defers the check to runtime. ## How the two array parameters surface For `void copy(Object[] dest, int[] src)`: - `int[] src` -> **`IntArray!`** (a platform `IntArray`). Specialized primitive array — you pass `IntArray`, e.g. `intArrayOf(...)`. Passing `Array<Int>` is a compile error; convert with `.toIntArray()`. - `Object[] dest` -> **`Array<(out) Any!>!`**. Three things stacked: - `Any!` element — platform-typed elements (could be null). - `(out)` — an out-projection, so Java's covariance is honored: you can pass `Array<String>` where `Array<(out) Any!>` is expected. - trailing `!` — the array reference itself is platform-typed. ```kotlin // Java: void copy(Object[] dest, int[] src) val dest: Array<Any?> = arrayOfNulls(3) val src: IntArray = intArrayOf(1, 2, 3) JavaCopier.copy(dest, src) // OK JavaCopier.copy(arrayOf("a"), src) // OK too — (out) projection allows Array<String> // JavaCopier.copy(dest, arrayOf(1)) // ERROR — Array<Int> != int[] ``` ## Gotchas to watch 1. **Nullability is unchecked at the boundary.** Because the parameter is platform-typed, Kotlin won't stop you passing `null` or a nullable array; if Java dereferences it you get a runtime NPE. Decide and document the contract. 2. **ArrayStoreException still lurks.** The `(out)` projection mirrors Java covariance; if Java writes the wrong element type into a more specific array you handed it, the JVM throws `ArrayStoreException` at runtime — invariance only protects pure-Kotlin paths. 3. **Primitive vs boxed.** The single most common compile error here is passing `Array<Int>` to an `int[]` parameter; use `IntArray`/`.toIntArray()`. 4. **Improve the types by annotating Java.** Adding `@NotNull`/`@Nullable` (or adopting **JSpecify** / `@NonNull` packages) makes Kotlin infer real nullable/non-null array and element types instead of platform types, restoring compile-time null safety. 5. **Generics + arrays.** Java `T[]` of a generic surfaces with `(out)` projection too; reading is fine, writing through the projected view is restricted in Kotlin. ## Summary - Java arrays come in as platform types: `IntArray!`, `Array<(out) Any!>!`. - `(out)` = honor Java covariance for passing; restrict Kotlin-side writes. - Watch nullability (runtime NPE), ArrayStoreException, and primitive/boxed mismatch. - Annotate the Java side (JSpecify) to recover precise, null-safe Kotlin types.
- What does the (out) in Array<(out) Any!> let you do, and what does it forbid?It lets you pass a more specific array (Array<String>) into the Java Object[] parameter, mirroring Java covariance; it forbids writing arbitrary elements through that projected view in Kotlin to keep stores type-safe.
- How do you get rid of the platform types so the compiler enforces nullability?Annotate the Java API with @Nullable/@NotNull or adopt JSpecify @NonNull/@Nullable package defaults; Kotlin then infers concrete nullable or non-null array and element types.
A platform-typed Java array is a parcel with no 'fragile/non-fragile' sticker — Kotlin lets you treat it either way, but if you guess wrong the breakage shows up at delivery (runtime).
saying these in an interview costs you the question
- Saying Java arrays come in as fully non-null Kotlin types by default
- Passing Array<Int> to an int[] parameter
- Claiming Kotlin invariance prevents all ArrayStoreException at runtime
- Not knowing platform types defer null checks to runtime