copy() takes each constructor property as a named argument defaulting to its current value, so you name only what changes, like { ...book, price: 21.6 } in JavaScript. val (a, b) = x calls x.component1() and x.component2(), generated in constructor order (as in Loops and Ranges).
data class Book(val id: Int, val title: String, val price: Double, val inStock: Boolean = true)
data class Shelf(val name: String, val books: MutableList<Book>)
fun main() {
val book = Book(3, "Salt and Saffron", 24.00, inStock = false)
val sale = book.copy(price = 21.60, inStock = true)
println(sale)
val (id, title, price) = sale // component1() .. component3()
val (_, cost) = sale // position 2 is title, not price!
println("#$id $title $price, cost = $cost")
val mine = Shelf("To read", mutableListOf(book))
val gifts = mine.copy(name = "Gift ideas") // shallow: the list is shared
gifts.books += sale
println("${mine.books.size} ${mine.books === gifts.books}")
}Book(id=3, title=Salt and Saffron, price=21.6, inStock=true) #3 Salt and Saffron 21.6, cost = Salt and Saffron 2 true
Two traps. Destructuring is positional, so cost silently got the title, and copy() is shallow: both shelves share one MutableList. Use a read-only List and replace it: copy(books = books + sale).
Kotlin is fixing the first trap. With the experimental -Xname-based-destructuring=name-mismatch, kotlinc 2.4.20 warns "variable name 'cost' differs from accessed property name 'title'", and it accepts (val cost = price) = sale, which binds by name (complete mode adds val [id, title] = sale for position). Without the option both forms are errors ("only available since language version 2.5").