Kotlin Destructuring Declarations: Unpacking Objects into Variables
Destructuring declarations provide a concise way to extract multiple values from an object and assign them to separate variables.
val (name, age) = person
This syntax declares two new variables—name and age—in a single statement. Each can be used independently afterward:
println(name)
println(age)
Under the hood, the compiler trenslates this into calls to componentN() functions:
val name = person.component1()
val age = person.component2()
These component functions follow Kotlin’s operator conventions—similar to how +, *, or for loops rely on specific named functions. To be usable in destructuring, each componentN() must be marked with the operator modifier.
Destructuring also works inside for loops. For example:
for ((code, description) in items) {
println("$code: $description")
}
Here, code receives the result of items[i].component1(), and description receives items[i].component2().
Returning Multiple Values from Functions
Data classes are ideal for returning grouped results beecause they automatically generate componentN() functions:
data class ApiResult(val data: String, val httpCode: Int)
fun fetchResponse(): ApiResult {
return ApiResult("Success", 200)
}
val (payload, code) = fetchResponse() // Destructuring in action
While Pair<String, Int> could serve the same purpose, domain-specific data classes improve readability and type safety.
Destructuring Maps
Iterating over maps becomes more expressive with destructuring:
for ((key, value) in configurationMap) {
process(key, value)
}
This works because Kotlin’s standard library provides extension functions like:
operator fun <K, V> Map<K, V>.iterator(): Iterator<Map.Entry<K, V>> = entrySet().iterator()
operator fun <K, V> Map.Entry<K, V>.component1(): K = key
operator fun <K, V> Map.Entry<K, V>.component2(): V = value
Ignoring Components with Underscores
When only some parts of a destructured value are needed, replace unused names with _:
val (_, errorCode) = performOperation()
No component1() call occurs in this case—the compiler skips it entirely.
Destructuring in Lambda Parameters
Lambda expressions support destructuring directly in thier parameter lists:
// Without destructuring
map.mapValues { entry -> "${entry.value.toUpperCase()}" }
// With destructuring
map.mapValues { (key, value) -> "$value!" }
You can mix destructured and regular parameters:
listOfPairs.map { (first, second), index -> "$first-$second@$index" }
Unused components may also be replaced by underscores inside lambdas:
map.filterKeys { (_, value) -> value.isNotEmpty() }
Type annotations can apply to the entire destructured parameter or individual components:
map.mapValues { (_, value): Map.Entry<String, String> -> value.uppercase() }
map.mapValues { (_, value: String) -> value.trim() }