Kotlin Array to Java varargs: how not to break compatibility and write clean code
\n\nHave you ever written code in Kotlin, called a good old Java method with varargs — and suddenly got a compilation error? If so, you are not alone. The question "Convert Kotlin Array to Java varargs" on Stack Overflow has thousands of views. And it's not surprising: at the intersection of two languages, non-obvious nuances often arise.
In this article, we will break down how to correctly pass a Kotlin array to a Java method with a variable number of arguments. You will learn what the spread operator is, why Array<String> is not equal to String..., and how to avoid typical mistakes. Let's go!
What are varargs in Java and why are they needed
\n\nFirst, let's recall the basics. Varargs (variable-length arguments) is a Java mechanism that allows you to pass an arbitrary number of arguments of the same type to a method. Syntactically, it is denoted by an ellipsis after the type:
\n\npublic void printMessages(String... messages) {\n for (String msg : messages) {\n System.out.println(msg);\n }\n}\n\nYou can call this method like this:
\n\nprintMessages("Hello");\nprintMessages("Hello", "World");\nprintMessages(); // an empty call also works\n\nInside the method, the arguments are represented as an array String[]. But from the outside, it's just a set of values separated by commas. Convenient, right?
Now imagine you have a Kotlin Array and you want to pass it to such a Java method. Naive code won't work:
\n\nval messages = arrayOf("Hello", "World")\nprintMessages(messages) // Compilation error!\n\nWhy? Because Kotlin is stricter with types. The Java method expects String..., but you are passing Array<String>. These are different things. The compiler sees the mismatch and throws an error.
Solution: the spread operator (* operator)
\n\nIn Kotlin, for such cases, there is the spread operator — an asterisk * before the array. It literally "unfolds" the array into individual elements, which are then passed as varargs.
val messages = arrayOf("Hello", "World")\nprintMessages(*messages) // Works!\n\nEverything ingenious is simple. But let's break down how it works under the hood.
\n\nHow the spread operator transforms types
\n\nWhen you write *messages, Kotlin:
- \n
- Takes each element of the array \n
- "Extracts" them from the
Arraywrapper \n - Passes them as separate arguments to the Java method \n
For the Java method, it looks as if you wrote printMessages("Hello", "World"). No arrays — just plain strings.
An important nuance: the spread operator only works with Array (and some other collections via toTypedArray()). You cannot unfold a regular List this way.
Typical mistakes and how to avoid them
\n\nEven knowing about the spread operator, beginners often make mistakes. Here are the most common ones.
\n\nMistake 1: Passing an array without the asterisk
\n\n// Incorrect:\nval numbers = intArrayOf(1, 2, 3)\nsomeJavaMethod(numbers) // Error\n\n// Correct:\nsomeJavaMethod(*numbers.toTypedArray())\n\nNote: for primitive types (IntArray, DoubleArray, etc.), you first need to convert to Array using .toTypedArray(). The spread operator only works with object arrays.
Mistake 2: Confusion with List
\n\nMany try to pass a List<String> directly:
val list = listOf("A", "B")\nprintMessages(*list) // Error! List != Array\n\nSolution: first convert the list to an array:
\n\nprintMessages(*list.toTypedArray())