Как передать Kotlin Array в Java varargs: полный гайд для новичков

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Kotlin Array в Java varargs: как не сломать совместимость и написать чистый код

Вы когда-нибудь писали на Kotlin, вызывали старый добрый Java-метод с varargs — и вдруг получали ошибку компиляции? Если да, то вы не одиноки. Вопрос "Convert Kotlin Array to Java varargs" на Stack Overflow набрал тысячи просмотров. И это неудивительно: на стыке двух языков часто возникают неочевидные нюансы.

В этой статье мы разберём, как правильно передать массив Kotlin в Java-метод с переменным числом аргументов. Вы узнаете, что такое spread-оператор, почему Array<String> не равен String... и как избежать типичных ошибок. Поехали!

Что такое varargs в Java и зачем они нужны

Для начала вспомним основы. Varargs (variable-length arguments) — это механизм Java, который позволяет передавать в метод произвольное количество аргументов одного типа. Синтаксически это обозначается троеточием после типа:

public void printMessages(String... messages) {
    for (String msg : messages) {
        System.out.println(msg);
    }
}

Вы можете вызвать этот метод так:

printMessages("Hello");
printMessages("Hello", "World");
printMessages(); // пустой вызов тоже работает

Внутри метода аргументы представлены как массив String[]. Но снаружи — это просто набор значений, разделённых запятыми. Удобно, правда?

А теперь представьте, что у вас есть Kotlin Array, и вы хотите передать его в такой Java-метод. Наивный код не сработает:

val messages = arrayOf("Hello", "World")
printMessages(messages) // Ошибка компиляции!

Почему? Потому что Kotlin строже относится к типам. Java-метод ожидает String..., а вы передаёте Array<String>. Это разные вещи. Компилятор видит несоответствие и выдаёт ошибку.

Решение: spread-оператор (оператор *)

В Kotlin для таких случаев существует spread-оператор — звёздочка * перед массивом. Он буквально «разворачивает» массив в отдельные элементы, которые затем передаются как varargs.

val messages = arrayOf("Hello", "World")
printMessages(*messages) // Работает!

Всё гениальное просто. Но давайте разберёмся, как это работает под капотом.

Как spread-оператор преобразует типы

Когда вы пишете *messages, Kotlin:

  • Берёт каждый элемент массива
  • «Вытаскивает» их из обёртки Array
  • Передаёт как отдельные аргументы в Java-метод

Для Java-метода это выглядит так, как будто вы написали printMessages("Hello", "World"). Никаких массивов — только чистые строки.

Важный нюанс: spread-оператор работает только с Array (и некоторыми другими коллекциями через toTypedArray()). Обычный List так не развернуть.

Типичные ошибки и как их избежать

Даже зная про spread-оператор, новички часто допускают ошибки. Вот самые частые из них.

Ошибка 1: Передача массива без звездочки

// Неправильно:
val numbers = intArrayOf(1, 2, 3)
someJavaMethod(numbers) // Ошибка

// Правильно:
someJavaMethod(*numbers.toTypedArray())

Заметьте: для примитивных типов (IntArray, DoubleArray и т.д.) нужно сначала преобразовать в Array через .toTypedArray(). Spread-оператор работает только с объектными массивами.

Ошибка 2: Путаница с List

Многие пытаются передать List<String> напрямую:

val list = listOf("A", "B")
printMessages(*list) // Ошибка! List != Array

Решение: сначала преобразуйте список в массив:

printMessages(*list.toTypedArray())

Ошибка 3: Изменение порядка аргументов

Spread-оператор можно использовать вместе с обычными аргументами, но только в конце списка (как и varargs в Java):

// Можно:
printMessages("First", *moreMessages)

// Нельзя:
printMessages(*moreMessages, "Last") // Ошибка компиляции

Практический пример: смешиваем Kotlin и Java

Давайте соберём всё вместе. Предположим, у вас есть Java-класс с методом для форматирования строк:

// Java
public class StringFormatter {
    public static String format(String template, String... args) {
        return String.format(template, (Object[]) args);
    }
}

В Kotlin вы хотите передать динамический список аргументов:

// Kotlin
fun main() {
    val template = "Hello, %s! Today is %s."
    val words = arrayOf("Alice", "Monday")
    
    val result = StringFormatter.format(template, *words)
    println(result) // Hello, Alice! Today is Monday.
}

Обратите внимание: мы передаём *words, а не words. Если забыть звёздочку, компилятор выдаст ошибку: "Type mismatch: inferred type is Array<String> but String was expected".

А если массив пустой? Edge cases

Spread-оператор корректно обрабатывает пустые массивы:

val emptyArray = emptyArray<String>()
printMessages(*emptyArray) // Вызовется с 0 аргументов, всё ок

А что с null? Будьте осторожны:

val nullArray: Array<String>? = null
// printMessages(*nullArray) // NullPointerException!
// Лучше так:
printMessages(*nullArray.orEmpty())

Всегда проверяйте на null перед spread-оператором, иначе получите исключение в рантайме.

Совместимость с Java: что ещё нужно знать

Если вы пишете код на Kotlin, который должны вызывать Java-разработчики, помните: Kotlin-функции с vararg тоже используют spread-оператор при вызове из Java? Нет! Java видит Kotlin-vararg как массив:

// Kotlin
fun greet(vararg names: String) {
    names.forEach { println("Hello, $it") }
}

// Из Java:
String[] people = {"Alice", "Bob"};
greet(people); // Работает без spread!

Это ещё один повод помнить о разнице языков: Kotlin защищает от случайной передачи массива, а Java — нет.

Производительность: стоит ли беспокоиться?

Spread-оператор создаёт временную копию массива

Блоги

Рекомендаций по книгам