Введение: Почему этот вопрос мучает каждого, кто переходит с Java на Kotlin
Вы только начали изучать Kotlin после Java и уже столкнулись с первой неожиданностью? Вы пытаетесь объявить массив строк так, как привыкли в Java — String[] names = {"Alice", "Bob"} — а компилятор Kotlin выдаёт ошибку? Поздравляю, вы не одиноки. Вопрос "What's the Kotlin equivalent of Java's String[]?" собрал тысячи просмотров на Stack Overflow, и это не случайно.
Kotlin — это не просто «Java с сахаром». Google позиционирует его как более современный, безопасный и лаконичный язык. Но эта лаконичность иногда сбивает с толку новичков. В Java массив — это встроенная конструкция языка. В Kotlin же массивы — это часть стандартной библиотеки, и у них есть свои хитрости.
В этой статье мы разберём всё, что нужно знать о Kotlin массивах строк. Вы узнаете не только прямой аналог String[], но и более гибкие альтернативы, которые сделают ваш код чище и безопаснее. Поехали!
1. Прямой аналог: Array<String>
Самый простой и очевидный способ заменить String[] в Kotlin — это использовать Array<String>. В Kotlin все массивы являются объектами класса Array<T>, где T — тип элементов.
Как объявить и инициализировать?
В Java вы пишете:
String[] names = new String[3];
names[0] = "Alice";
names[1] = "Bob";
names[2] = "Charlie";
В Kotlin это выглядит так:
val names: Array<String> = arrayOfNulls(3)
names[0] = "Alice"
names[1] = "Bob"
names[2] = "Charlie"
Но есть способ гораздо короче — использовать функцию arrayOf():
val names = arrayOf("Alice", "Bob", "Charlie")
Компилятор Kotlin сам выведет тип Array<String>. Обратите внимание: в отличие от Java, в Kotlin нет оператора new для создания массивов. Вместо этого используются фабричные функции.
Важное отличие: фиксированный размер
Как и в Java, размер Array<String> фиксирован. Вы не можете добавить новый элемент после создания. Если попытаетесь обратиться к индексу за пределами длины, получите ArrayIndexOutOfBoundsException.
val fruits = arrayOf("Apple", "Banana")
// fruits[2] = "Cherry" // Ошибка! Индекс выходит за границы
2. Специализированные массивы: не всегда Array<String>
Для примитивных типов (Int, Double, Boolean) Kotlin предлагает оптимизированные классы: IntArray, DoubleArray, BooleanArray и т.д. Они не используют автоупаковку (boxing/unboxing), что экономит память и ускоряет выполнение.
Но для строк такой оптимизации нет, потому что String — это ссылочный тип, а не примитив. Поэтому Array<String> — это единственная опция, если вам нужен именно массив.
3. Альтернатива №1: List<String> (неизменяемый список)
В большинстве случаев вам не нужен массив. В Kotlin настоятельно рекомендуется использовать списки вместо массивов, если вы не работаете с низкоуровневым кодом (например, JNI или оптимизация производительности на уровне наносекунд).
Список List<String> — это неизменяемая коллекция. Вы не можете изменить её размер или элементы после создания. Это делает код безопаснее в многопоточной среде.
val namesList: List<String> = listOf("Alice", "Bob", "Charlie")
// namesList[0] = "Eve" // Ошибка! listOf возвращает неизменяемый список
По сути, listOf — это Kotlin эквивалент Java String[] с дополнительной защитой. В Java массив можно случайно изменить, передав его в другой метод. В Kotlin List этого не позволяет.
4. Альтернатива №2: MutableList<String> (изменяемый список)
Если вам нужна динамическая коллекция, к которой можно добавлять или удалять элементы, используйте MutableList<String>.
val mutableNames = mutableListOf("Alice", "Bob")
mutableNames.add("Charlie") // Добавляем элемент
mutableNames.removeAt(0) // Удаляем по индексу
println(mutableNames) // Вывод: [Bob, Charlie]
Это аналог ArrayList<String> в Java, но в Kotlin он короче и удобнее. mutableListOf() по умолчанию создаёт именно ArrayList.
5. Специальный случай: vararg (переменное количество аргументов)
Иногда в Java вы пишете метод, который принимает String... (varargs). В Kotlin это тоже поддерживается, но с небольшим синтаксическим отличием.
Java:
public void printNames(String... names) {
for (String name : names) {
System.out.println(name);
}
}
Kotlin:
fun printNames(vararg names: String) {
for (name in names) {
println(name)
}
}
// Вызов:
printNames("Alice", "Bob", "Charlie")
Внутри функции names ведёт себя как Array<String>. Вы даже можете передать существующий массив, используя оператор * (spread operator):
val array = arrayOf("Alice", "Bob")
printNames(*array)
Это очень удобно, когда нужно объединить фиксированный массив с переменным количеством аргументов.
6. Как выбрать: массив, список или mutableList?
Вот простая шпаргалка для начинающих:
- Нужен точный аналог Java String[] — используйте
Array<String>сarrayOf(). - Нужна неизменяемая коллекция (только чтение) — используйте
List<String>сlistOf(). - Нужна динамическая коллекция (добавление/удаление) — используйте
MutableList<String>сmutableListOf(). - Пишете функцию с переменным числом аргументов — используйте
vararg.
Золотое правило Kotlin: Если не уверены, выбирайте List (неизменяемый). Он безопаснее и более идиоматичен для Kotlin. Массивы (Array) оставьте для случаев, когда вам критична производительность или вы работаете с Java-кодом.