Destructuring declaration — що це, синтаксис і застосування в Kotlin

Автор: IT Sectr Опубліковано: 2026-06-23 Час читання: 7 хв

Destructuring declaration — це можливість Kotlin розкласти об'єкт на кілька змінних в одному виразі. Конструкція val (name, age) = user одночасно оголошує дві змінні, присвоюючи їм значення з компонентів об'єкта. За даними документації Kotlin (2026), деструктуризація скорочує обсяги коду під час роботи з data class на 30–50%. Destructuring declaration робить код більш лаконічним і читабельним.

Головне

  • Destructuring declaration — синтаксис val (a, b) = obj для розкладання об'єкта на змінні
  • ComponentN() — функції component1(), component2(), component3() і так далі, що викликаються автоматично
  • Data class автоматично генерує componentN() для всіх властивостей, оголошених у primary constructor
  • Pair і Triple — стандартні класи з готовою деструктуризацією для двох і трьох значень
  • Map.Entry — підтримує деструктуризацію на key і value для зручного обходу

Що таке destructuring declaration?

Destructuring declaration (деструктуризуюче оголошення) — це синтаксична конструкція Kotlin, що дозволяє розкласти екземпляр класу на кілька змінних за один вираз. Ліва частина містить список змінних у дужках, права — об'єкт, що розкладається. Kotlin автоматично викликає для кожної змінної відповідну функцію componentN(), де N — порядковий номер змінної в дужках.

Найчастіше destructuring declaration застосовується з data class. Компілятор Kotlin автоматично генерує для data class функції component1(), component2() і так далі — за кількістю властивостей у первинному конструкторі. Для звичайних класів без модифікатора data componentN() не генеруються, але їх можна визначити вручну як операторні функції з ключовим словом operator.

Destructuring declaration стала популярним патерном завдяки своїй здатності спрощувати код під час роботи з колекціями пар, результатами функцій та обходом Map. Замість звернення person.firstName, person.lastName розробник пише val (firstName, lastName) = person. Це скорочує кількість рядків і підвищує читабельність, особливо в циклах і лямбдах.

Як працює деструктуризація в Kotlin?

При зустрічі з конструкцією val (a, b) = obj компілятор Kotlin генерує код: val a = obj.component1(); val b = obj.component2(). Якщо змінні оголошені з var, генеруються var-присвоєння. Тип кожної змінної виводиться з типу, що повертає відповідна функція componentN(). Це означає, що destructuring declaration повністю типобезпечна і перевіряється на етапі компіляції.

kotlin
data class Person(val firstName: String, val lastName: String, val age: Int)

fun main() {
    val person = Person("Alice", "Smith", 30)

    // Деструктуризація: component1(), component2(), component3()
    val (firstName, lastName, age) = person
    println("$firstName $lastName, $age років")
}

У лістингу Person — data class із трьома властивостями. Kotlin генерує component1() → firstName, component2() → lastName, component3() → age. Вираз val (firstName, lastName, age) = person викликає ці функції та присвоює результати. Важливо: імена змінних можуть не збігатися з іменами властивостей — компілятор дивиться лише на порядок componentN() та їхні типи. Можна розкласти лише перші N властивостей, ігноруючи решту.

Деструктуризація data class і стандартних типів

Data class — основний кандидат для destructuring declaration. Кожна data class автоматично отримує componentN() для всіх властивостей первинного конструктора. Порядок componentN() відповідає порядку оголошення властивостей. Це означає, що при зміні порядку полів у data class змінюється порядок змінних у деструктуризації — що може призвести до непомітних багів.

kotlin
// Деструктуризація Pair
val pair = Pair("key", 42)
val (key, value) = pair
println("$key -> $value")

// Деструктуризація Triple
val triple = Triple("A", "B", "C")
val (first, second, third) = triple

// Map.Entry у циклі
val map = mapOf("a" to 1, "b" to 2)
for ((k, v) in map) {
    println("$k = $v")
}

Pair і Triple — стандартні класи з готовою деструктуризацією на два і три компоненти. Pair часто використовується як тип, що повертають функції, яким потрібно повернути два пов'язані значення без створення окремої data class. У циклі for по Map.Entry конструкція (k, v) = entry деструктуризує ключ і значення через component1() і component2(). Це стандартний патерн обходу Map у Kotlin, що повністю замінює явні виклики key і value.

Підкреслення для невикористовуваних змінних

Якщо при деструктуризації деякі компоненти не потрібні, Kotlin дозволяє замінити їх на підкреслення _. Це покращує читабельність — читач одразу бачить, що компонент ігнорується. Крім того, використання _ запобігає випадковому зверненню до неініціалізованої змінної: компілятор не створює для _ жодної змінної, що усуває попередження про невикористовувані змінні.

kotlin
data class Order(
    val id: Long,
    val customerName: String,
    val amount: Double,
    val status: String
)

fun process(orders: List<Order>) {
    for ((id, _, amount) in orders) {
        println("Замовлення $id: $amount")
    }
}

У лістингу Order має чотири властивості, але в циклі потрібні лише id і amount. Підкреслення на місці customerName каже компілятору «пропусти component2()». Kotlin не викликає component2() і не створює змінної для цього компонента. Важливо: підкреслення можна використовувати лише в destructuring declaration, але не в data class-конструкторі. Якщо пропускається кілька компонентів підряд, кожному відповідає своє підкреслення.

Створення componentN() для власних класів

Для класів без модифікатора data можна вручну визначити componentN(), додавши ключове слово operator. Це дозволяє використовувати destructuring declaration з будь-яким класом, включно зі сторонніми, для яких не можна змінити вихідний код — достатньо написати extension-функцію з operator. Такий підхід особливо корисний для бібліотечних класів і класів із Java-коду, у яких немає автоматичних componentN().

kotlin
class Rect(val width: Int, val height: Int)

operator fun Rect.component1(): Int = width
operator fun Rect.component2(): Int = height

// Для Java-класу з геттерами
operator fun Dimension.component1(): Int = width
operator fun Dimension.component2(): Int = height

fun main() {
    val rect = Rect(1920, 1080)
    val (w, h) = rect
    println("Роздільна здатність: ${w}x${h}")
}

У прикладі Rect — звичайний клас без data. Extension-функції з operator fun component1() = width і component2() = height додають підтримку деструктуризації. Аналогічно для java.awt.Dimension: operator extension на Dimension дозволяє розкласти його на width і height. Обмеження: максимальна кількість компонентів для destructuring declaration — 5. Теоретично можна визначити component6() і вище, але Kotlin не згенерує відповідний синтаксис для 6+ змінних у дужках.

Часті запитання

Чи завжди data class підтримує деструктуризацію?

Так, кожна data class автоматично отримує componentN() для всіх властивостей первинного конструктора. Властивості, оголошені в тілі класу (через конструктор без val/var), не породжують componentN().

Що станеться при незбігу кількості змінних?

Якщо в val (a, b, c) = obj об'єкт має лише дві функції component, компілятор видасть помилку. Якщо змінних менше (val (a) = pair), Kotlin викличе лише component1() і проігнорує component2() — помилки не буде.

Чи можна деструктуризувати Map без циклу?

Так, Map.Entry підтримує деструктуризацію: val (key, value) = map.entries.first(). Для всієї Map одразу деструктуризація не працює — лише поелементно, у циклі for або через ітератор.

Чи працює деструктуризація в лямбдах?

Так, Kotlin підтримує деструктуризацію в параметрах лямбди: map.forEach { (k, v) -> ... }. Для data class у параметрах лямбди потрібно додавати вкладені дужки навколо параметра.

Який максимальний розмір деструктуризації?

Kotlin підтримує до 5 змінних у destructuring declaration. Якщо потрібно розкласти більше п'яти властивостей, об'єднайте їх у вкладені data class або використовуйте Pair/Triple всередині однієї змінної.

Підсумки

  • Destructuring declaration — синтаксис val (a, b) = obj, що розкладає об'єкт на кілька змінних в одному виразі
  • ComponentN() — функції component1(), component2(), ... що викликаються компілятором автоматично при деструктуризації
  • Data class автоматично генерує componentN() для всіх властивостей первинного конструктора
  • Pair і Triple — стандартні класи з готовою деструктуризацією на 2 і 3 компоненти
  • Підкреслення _ замінює невикористовувані компоненти без створення змінної
  • Operator componentN() в extension-функціях додає деструктуризацію будь-яким класам, включно з Java
  • Map.Entry у циклі for ((k, v) in map) — стандартний ідіоматичний патерн Kotlin для обходу словника

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також