Destructuring (val (a, b) = …) în Kotlin

Funcții și lambda Kotlin 1.0 (în parametrii lambda: Kotlin 1.1)

Destructuring desface un obiect în mai multe variabile deodată: val (nume, nota) = pereche.

Sintaxă
val (nume, varsta) = persoana
for ((cheie, valoare) in map) { ... }
lista.map { (nume, nota) -> ... }
val (_, oras) = adresa          // _ sare peste o componentă

Ce face Destructuring (val (a, b) = ...)

Declarația val (a, b) = obiect creează două variabile și le dă valorile obiect.component1() și obiect.component2(). Funcțiile componentN există automat pentru data class (în ordinea proprietăților din constructor), Pair, Triple, elementele unui Map și liste (primele cinci elemente).

Destructuring merge în for (for ((oras, temp) in temperaturi)), în parametrii unei lambda (map { (nume, nota) -> ... }) și la valorile returnate de funcții (o funcție poate returna un Pair sau o data class). O componentă care nu te interesează se înlocuiește cu _.

Destructuring se face după poziție, nu după nume: în val (oras, nume) = persoana, oras primește tot prima proprietate. Pentru clasele tale poți scrie funcții operator fun component1().

Exemple

Exemplul 1: Data class, Pair și funcție care returnează două valori

minMax returnează un Pair, desfăcut direct în două variabile.

data class Elev(val nume: String, val clasa: String, val medie: Double)

fun minMax(note: List<Int>): Pair<Int, Int> = note.min() to note.max()

fun main() {
    val elev = Elev("Ana Pop", "a X-a B", 9.45)
    val (nume, clasa, medie) = elev
    println("$nume este în clasa $clasa și are media $medie")

    val (minim, maxim) = minMax(listOf(7, 10, 5, 9))
    println("Nota minimă: $minim, maximă: $maxim")
}

Rezultat

Ana Pop este în clasa a X-a B și are media 9.45
Nota minimă: 5, maximă: 10

Exemplul 2: În for și în lambda, cu _

Elementele unui Map se desfac în cheie și valoare. În lambda, parantezele din jurul lui (nume, _, medie) arată destructuring; _ sare peste clasă.

data class Elev(val nume: String, val clasa: String, val medie: Double)

fun main() {
    val temperaturi = mapOf("Iași" to 14, "Cluj" to 11, "Constanța" to 17)
    for ((oras, grade) in temperaturi) println("$oras: $grade °C")

    val elevi = listOf(Elev("Ana", "X B", 9.5), Elev("Radu", "X A", 8.25))
    elevi.forEach { (nume, _, medie) -> println("$nume -> $medie") }
}

Rezultat

Iași: 14 °C
Cluj: 11 °C
Constanța: 17 °C
Ana -> 9.5
Radu -> 8.25

Exemplul 3: componentN pentru o clasă proprie

Clasa Interval nu este data, dar definește component1 și component2 cu operator, deci poate fi desfăcută.

class Interval(val start: Int, val sfarsit: Int) {
    operator fun component1() = start
    operator fun component2() = sfarsit
}

fun main() {
    val program = Interval(9, 17)
    val (deschide, inchide) = program
    println("Magazinul este deschis între $deschide și $inchide (${inchide - deschide} ore)")

    val (primul, alDoilea) = listOf("aur", "argint", "bronz")
    println("Locul 1: $primul, locul 2: $alDoilea")
}

Rezultat

Magazinul este deschis între 9 și 17 (8 ore)
Locul 1: aur, locul 2: argint

Greșeli frecvente

  • Schimbarea ordinii proprietăților într-o data class: toate destructurările existente primesc alte valori, fără nicio eroare de compilare dacă tipurile se potrivesc.
  • Destructuring pe o clasă obișnuită fără componentN: eroare destructuring of type 'X' requires operator function 'component1()'.
  • Uitarea parantezelor în lambda: { nume, nota -> } înseamnă doi parametri separați, nu un Pair desfăcut; se scrie { (nume, nota) -> }.

Comenzi înrudite

  • data class: data class este o clasă pentru date: primește automat toString, equals, hashCode, copy și componentN.
  • Pair: O pereche de două valori, first și second; a to b este felul scurt de a crea un Pair, folosit în mapOf.
  • for: Parcurge pe rând elementele unei colecții, ale unui interval sau ale oricărui obiect care are iterator().
  • operator: operator marchează o funcție cu nume special (plus, get, contains, compareTo…) ca să poată fi folosită prin +, [], in sau <.
  • lambda: O lambda este o funcție fără nume, scrisă între acolade, pe care o poți pune într-o variabilă sau trimite altei funcții.