Kotlin List не содержит add? Ищем пропавшие методы

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

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

Начать курс

Почему Kotlin "забрал" у нас add, remove и put? Разбираемся раз и навсегда

Если вы когда-нибудь писали на Java, а потом перешли на Kotlin, то наверняка сталкивались с этим моментом лёгкого недоумения. Вы создаёте List, хотите добавить в него элемент — пишете .add(), а среда разработки подсвечивает красным. Или пытаетесь положить значение в Map через .put() — и снова ошибка.

Знакомо? Ещё бы! Вопрос "Kotlin's List missing "add", "remove", Map missing "put", etc?" на Stack Overflow собрал тысячи просмотров и десятки комментариев. Это настоящий камень преткновения для новичков, и даже опытные разработчики иногда ловят себя на мысли: "А где же мои привычные методы?"

В этой статье мы не просто ответим на этот вопрос. Мы покажем, как философия Kotlin превращает этот "недостаток" в ваше главное преимущество. Вы узнаете лучшие практики работы с коллекциями в 2025 году, научитесь выбирать правильный тип коллекции и перестанете путаться в listOf и mutableListOf. Поехали!

В чём соль? Неизменяемость как образ жизни

Первое, что нужно понять: Kotlin с самого начала проектировался как язык, который поощряет безопасный и предсказуемый код. Один из главных принципов — неизменяемость (immutability) по умолчанию.

В Java у вас есть один интерфейс List, и он всегда содержит методы add(), remove(), set() и так далее. Даже если вы создаёте список через List.of() (Java 9+), у вас всё равно есть доступ к этим методам — они просто кидают исключение во время выполнения. Это неудобно и небезопасно.

Kotlin пошёл другим путём. Он разделил коллекции на два типа:

  • Неизменяемые (read-only): List, Set, Map — у них нет методов, изменяющих содержимое.
  • Изменяемые (mutable): MutableList, MutableSet, MutableMap — вот у них есть все знакомые add, remove, put.

Именно поэтому listOf(1, 2, 3) не имеет метода .add(). Он возвращает неизменяемый список. А mutableListOf(1, 2, 3) — возвращает изменяемый, и у него .add() есть.

Звучит логично? Давайте посмотрим на код.

Примеры: что работает, а что нет

Вот классическая ситуация, которая путает новичков:

// ❌ Ошибка! У List нет add()
val fruits = listOf("apple", "banana")
fruits.add("orange") // Ошибка компиляции

// ✅ Правильно: используем MutableList
val mutableFruits = mutableListOf("apple", "banana")
mutableFruits.add("orange") // Теперь работает!
println(mutableFruits) // [apple, banana, orange]

С Map та же история:

// ❌ Ошибка! У Map нет put()
val capitals = mapOf("Russia" to "Moscow", "France" to "Paris")
capitals.put("Germany", "Berlin") // Ошибка компиляции

// ✅ Правильно: используем MutableMap
val mutableCapitals = mutableMapOf("Russia" to "Moscow", "France" to "Paris")
mutableCapitals.put("Germany", "Berlin")
// Или ещё короче:
mutableCapitals["Germany"] = "Berlin"
println(mutableCapitals) // {Russia=Moscow, France=Paris, Germany=Berlin}

Обратите внимание: для MutableMap можно использовать операторные скобки [] — это и есть put() под капотом. Очень удобно!

Но зачем? Почему Kotlin так усложняет жизнь?

На первый взгляд кажется, что это лишняя головная боль. Зачем запоминать два типа коллекций? На самом деле, это гениальное решение. Вот три главные причины:

1. Безопасность на уровне типов

Компилятор теперь следит за тем, чтобы вы случайно не изменили список, который должен быть константой. Если ваша функция принимает List<Int>, вы можете быть уверены: внутри функции список не будет изменён. Это исключает целый класс багов, связанных с побочными эффектами.

// Функция гарантированно не меняет список
fun printFirstElement(list: List<String>) {
    // list.add("new") // Это даже не скомпилируется!
    println(list.firstOrNull())
}

2. Лучшая производительность и потокобезопасность

Неизменяемые коллекции можно безопасно использовать в многопоточных средах. Вам не нужны дополнительные синхронизации или блокировки. Kotlin идёт дальше — он предлагает listOf(), который возвращает оптимизированную реализацию, иногда даже без выделения лишней памяти.

3. Чистота кода и намерения

Когда вы видите List в сигнатуре функции, вы сразу понимаете: эти данные только для чтения. Если вы видите MutableList — значит, функция будет их менять. Код становится самодокументируемым.

Лучшие практики 2025: как не ошибиться

Теперь, когда мы разобрались с теорией, давайте перейдём к практике. Вот несколько золотых правил, которые помогут вам писать чистый и эффективный код на Kotlin в 2025 году.

Правило №1: По умолчанию используйте неизменяемые коллекции

Всегда начинайте с listOf(), setOf(), mapOf(). Переходите на mutable версии только тогда, когда это действительно нужно. Это дисциплинирует вас и делает код безопаснее.

// Плохо: mutable "на всякий случай"
val numbers = mutableListOf(1, 2, 3)
// ... 100 строк кода ...
// numbers.add(4) // А нужно ли это было?

// Хорошо: immutable по умолчанию
val numbers = listOf(1, 2, 3)
// Если понадобится изменить — создаём новую коллекцию
val extendedNumbers = numbers + 4

Правило №2: Используйте функции-расширения для трансформаций

Kotlin предлагает мощный набор функций для работы с коллекциями без их изменения. map, filter, associate, groupBy — это ваши новые друзья. Они возвращают новые коллекции, оставляя исходные нетронутыми.

val numbers = listOf(1, 2, 3, 4, 5)

// Вместо изменения списка — создаём новый
val doubled = numbers.map { it * 2 }
val evenNumbers = numbers.filter { it                

Блоги

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