skip to content

Calling Java From Kotlin

What Java looks like from the Kotlin side: relaxed nullability, getters that appear as properties, lambdas that satisfy SAM interfaces, and collections that arrive mutable. These are the daily details of working in a mixed codebase.

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

explore

questions

30

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

In Kotlin, what is the difference between List and MutableList, and where does that distinction actually exist at runtime?

level: juniorimportance: must knowfreq 70%

basics

~20 s

List has no add or remove methods, MutableList does. But it's only a rule the compiler checks. At runtime both are usually the same Java ArrayList, so the read-only promise can be broken from Java code.

open as a page

When you call a Java class from Kotlin, how do you read a value exposed by a Java getter like getName()? Show both ways Kotlin lets you access it.

level: juniorimportance: must knowfreq 80%

basics

~10 s

Kotlin lets you write obj.name instead of obj.getName(). It turns Java get/set methods into a property you read and write with a dot. You can still call obj.getName() the old way too.

open as a page

How do you call a Java static method and read a Java static field (like Integer.parseInt or Integer.MAX_VALUE) from Kotlin?

level: juniorimportance: must knowfreq 75%

basics

~10 s

You call them directly on the class name, just like in Java: Integer.parseInt("42") and Integer.MAX_VALUE. There is no special import or wrapper needed.

open as a page

What is a Kotlin platform type, why does it show up as String! in the IDE, and what happens to null checking when you use one?

level: juniorimportance: must knowfreq 72%

basics

~20 s

A platform type is a value coming from Java whose nullability Kotlin can't know. The compiler relaxes null checks for it. If you treat it as non-null but it's actually null, you get a NullPointerException at runtime.

open as a page

What is a SAM conversion in Kotlin, and how does it let you pass a lambda where a Java interface like Runnable or Comparator is expected?

level: juniorimportance: must knowfreq 70%

basics

~20 s

A SAM conversion lets you pass a short Kotlin lambda where Java expects an interface with exactly one method, like Runnable or Comparator. Kotlin turns the lambda into that interface automatically, so you skip the boilerplate anonymous class.

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

When a Java method returns java.util.List<String>, what Kotlin type does it appear as, and what are the safety implications of that mapping?

level: middleimportance: must knowfreq 65%

basics

~20 s

It usually shows up as a MutableList, so Kotlin lets you add and remove from it. But Java may not expect that list to change, so calling add could throw or corrupt the caller's data.

open as a page

Java's IOException is a checked exception. What happens in Kotlin when you call a Java method that declares `throws IOException`?

level: middleimportance: must knowfreq 70%

basics

~10 s

Nothing is forced. Kotlin has no checked exceptions, so the compiler does not make you write try/catch or declare the exception. You may catch it if you want, but you don't have to.

open as a page

How do @Nullable / @NotNull annotations on Java code change how Kotlin sees those values, and which annotation libraries does Kotlin recognize?

level: middleimportance: must knowfreq 60%

basics

~20 s

If the Java code is annotated with @Nullable or @NotNull, Kotlin stops treating the value as a loose platform type and treats it as String? or String. Kotlin recognizes several common annotation libraries like JetBrains, JSR-305, and AndroidX.

open as a page

Why can't you SAM-convert a lambda for a Kotlin interface by default, and how do you enable it?

level: middleimportance: must knowfreq 60%

basics

~10 s

By default Kotlin only auto-converts lambdas for Java single-method interfaces. For a Kotlin interface you mark it 'fun interface'. Without that, you must write a full object expression implementing the interface.

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

What does `.toList()` do versus simply upcasting a MutableList to List, and when must you prefer toList()?

level: middleimportance: should knowfreq 50%

basics

~20 s

Upcasting just changes the reference type but keeps the same object, so changes still leak through. toList() makes a brand-new copy, so later changes to the original don't affect it. Use toList() when you need a stable, independent snapshot.

open as a page

A Java class has a method `boolean isEnabled()` and `void setEnabled(boolean)`. What does the synthetic Kotlin property look like, and what's special about the `is` prefix rule?

level: middleimportance: should knowfreq 55%

basics

~10 s

Kotlin sees it as a property called enabled — but wait, no: for is-prefixed getters Kotlin keeps the whole name. The property is isEnabled, accessed as obj.isEnabled, and set via setEnabled.

open as a page

Which Java methods do NOT become synthetic Kotlin properties? Give the rules that disqualify a method, and what happens with getter/setter type mismatches.

level: middleimportance: should knowfreq 45%

basics

~20 s

Only methods shaped exactly like JavaBeans accessors become properties. A getter with parameters, a void/Unit getter, or a wrong name (like fetchName) stays a plain method. If getter and setter types disagree, you only get a read-only property.

open as a page

A Java class exposes a public instance field `public int count;` with no getter/setter. How do you read and write it from Kotlin, and how does that differ from a JavaBean property?

level: middleimportance: should knowfreq 50%

basics

~10 s

You access a public Java field directly by name: obj.count to read and obj.count = 5 to write. It looks just like a Kotlin property even though Java has no getter or setter.

open as a page

What is a SAM constructor (e.g. `Runnable { ... }`), and when do you need to use it explicitly instead of a bare lambda?

level: middleimportance: should knowfreq 45%

basics

~20 s

A SAM constructor wraps a lambda into a specific interface by writing the interface name before the lambda, like Runnable { ... }. You use it when the target type is ambiguous — for overloads, generics, or storing the value in a variable.

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

You declare `fun process(items: List<String>)` in Kotlin and call it from Java. Can the Java caller add elements to that list? Explain.

level: seniorimportance: should knowfreq 45%

basics

~10 s

Yes. Kotlin's read-only List is just a java.util.List to Java, so Java sees the normal add and remove methods and can change the list, even though Kotlin marked the parameter as read-only.

open as a page

From Kotlin you extend a Java class that has getValue()/setValue(). How can you override the accessor behavior, and what are the rules for overriding a Java getter as a Kotlin property vs. a function?

level: seniorimportance: should knowfreq 30%

basics

~20 s

In Kotlin you can override the inherited Java accessor pair as a property — declare override var value and supply custom get()/set(). You may also still override the raw methods, but using the property form is the idiomatic way.

open as a page

A Kotlin function throws a checked Java exception. Why might a Java caller fail to catch it, and how does @Throws fix this?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Because Kotlin doesn't record a throws clause in the compiled method, Java thinks the exception can't happen and refuses to let you catch it. Adding @Throws puts the clause back so Java can catch it.

open as a page

You consume a large unannotated Java library whose methods all return platform types. How do you design the Kotlin side so platform-type NPEs don't leak deep into your code?

level: seniorimportance: should knowfreq 34%

basics

~20 s

Wrap the Java library behind a thin Kotlin layer. At that boundary, give every value an explicit nullable or non-null type and validate it, so the rest of your code only ever sees proper Kotlin types and never raw platform types.

open as a page

What is JSpecify, how does it improve Java-to-Kotlin nullability over older annotations, and how does Kotlin's strict mode treat JSpecify-annotated code?

level: seniorimportance: should knowfreq 38%

basics

~10 s

JSpecify is a standard, vendor-neutral set of nullability annotations for Java. Kotlin understands it, including module-wide defaults and generic type-argument nullability, and can treat any mismatch as a compile error in strict mode.

open as a page

What surprising behaviors can SAM conversions cause around object identity, and how do you handle a Java method that takes a registered then-unregistered listener?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Each SAM-converted lambda may become a different object instance, so you can't reliably remove a listener by passing the 'same' lambda again. Capture the converted instance in a variable and reuse that exact reference for both add and remove.

open as a page

Name situations where SAM conversion does NOT apply, and explain how a Kotlin function type passed to Java differs from a SAM-converted lambda.

level: seniorimportance: should knowfreq 30%

basics

~20 s

SAM conversion needs a Java interface with exactly one abstract method as the target. It won't fire for abstract classes, multi-method interfaces, plain Kotlin interfaces, or when the parameter is already a Kotlin function type. A function type passed to Java surfaces as a FunctionN object, not your interface.

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

How would you expose a collection across a Java/Kotlin module boundary so that NO caller — Kotlin or Java — can mutate it?

level: principalimportance: nice to knowfreq 30%

basics

~10 s

A plain Kotlin read-only List isn't enough because Java can still change it. Use a defensive copy wrapped so mutation throws, or a truly immutable collection type whose add/remove always fail for everyone.

open as a page

What subtle pitfalls arise from Kotlin's synthetic-property mapping when consuming Java APIs — e.g. ambiguous names, the get/is collision, and how does this interact with the reverse (Kotlin properties seen from Java)?

level: principalimportance: nice to knowfreq 20%

basics

~20 s

Pitfalls include both getName() and isName() existing (ambiguity), names like getURL decapitalizing oddly, and confusion when going the other way: Kotlin properties appear to Java as getX()/setX() methods, so Java callers never see a 'property'.

open as a page

Given that Kotlin removes checked-exception enforcement, how do you keep error handling robust at module boundaries in a mixed Kotlin/Java codebase?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

Since the compiler won't force you, you have to be disciplined: catch failures where you can act on them, model expected errors as return values (like a Result or sealed type), and document or annotate what each boundary can throw.

open as a page

When you override a Java method that returns a platform type in Kotlin, what nullability must your override declare, and how do platform types interact with generic type parameters?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

When you override a Java method in Kotlin, you must pick a concrete nullability — nullable or non-null — for the return and parameters; you can't leave it as a platform type. With generics, Kotlin substitutes the platform nullability into the type argument the same way.

open as a page