skip to content

Destructuring Declarations

val (a, b) = pair works by position, calling the componentN operator functions, which is why it works for data classes, Pair, and Map.Entry. Interviewers ask because positional destructuring silently breaks when someone reorders a data class's properties.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What is a destructuring declaration in Kotlin, and what does `val (name, age) = person` actually do under the hood?

level: juniorimportance: must knowfreq 70%

answer

  1. Position-based, not name-based
  2. val (a,b) -> component1()/component2()
  3. data class auto-generates componentN()
  4. Pair/Triple/Map.Entry/arrays supported
  5. operator fun componentN()

basics

~10 s

It unpacks one object into several variables in a single line. Each variable is filled by calling the object's component functions in order: the first gets component1(), the second component2().

solid answer

~30 s

A destructuring declaration splits an object into multiple variables at once. `val (name, age) = person` is compiled to `val name = person.component1(); val age = person.component2()`. The position of each variable decides which `componentN()` operator function is called, not the variable's name. So names are arbitrary labels. The type must provide these `operator fun componentN()` functions — `data class` generates them automatically for its primary-constructor properties, and `Pair`, `Triple`, `Map.Entry`, and arrays/lists also provide them. If a type has no matching `componentN()`, the declaration won't compile.

code

kotlin · 7 lines
kotlin
data class Point(val x: Int, val y: Int)
val (x, y) = Point(3, 4)
println(x + y) // 7
// equivalent to:
val p = Point(3, 4)
val x2 = p.component1()
val y2 = p.component2()

go deeper

for a junior

Knows the syntax and that data classes support it; can read/write a simple destructuring line.

for a middle

Explains the componentN() rewrite and that binding is positional, not by name.

for a senior

Knows exactly which standard types provide componentN() and how to add operators to a custom class.

for a principal

Discusses API-design implications: positional binding makes destructuring brittle to property reordering, so cautions against destructuring wide data classes in public APIs.

## What it is A **destructuring declaration** binds several variables from a single object in one statement. It is purely syntactic sugar over operator function calls. ```kotlin data class Person(val name: String, val age: Int) val person = Person("Ada", 36) val (name, age) = person // name == "Ada", age == 36 ``` ## How it compiles The compiler rewrites the line positionally into calls to `componentN()` operator functions: ```kotlin val name = person.component1() val age = person.component2() ``` Key consequences: - **Position matters, names don't.** `val (age, name) = person` would put the *name* string into a variable you happened to call `age`. The compiler matches by slot 1, 2, 3..., never by property name. - The functions must be marked `operator fun componentN()`. ## Where the component functions come from - **`data class`** automatically generates `component1()`, `component2()`, ... for each property declared in its **primary constructor** (in declaration order). - **`Pair`/`Triple`** provide `component1()`/`component2()`(`/component3()`). - **`Map.Entry`** provides `component1()` (key) and `component2()` (value), enabling `for ((k, v) in map)`. - **Arrays and `List`** provide `component1()`..`component5()` by extension. - Any class can opt in by declaring the operators manually. ## When it does NOT work - A non-data class with no `componentN()` operators: `val (a, b) = someRegularObject` fails to compile. - More variables than available components also fails. ## Why it's useful Returning multiple values, iterating maps, and clean lambda parameters. It keeps code readable without temporary accessor calls.

  • If you swap the variable names in the parentheses, does the compiler reorder them to match the property names?
    No. Binding is strictly positional. The first variable always gets component1(), regardless of what you name it; mismatching names is a common source of silent bugs.
  • Can you destructure a regular (non-data) class?
    Only if it declares the operator fun componentN() functions itself. data classes get them for free; plain classes do not.

Like unpacking a labelled parcel by box order, not by the sticker you write on each item.

saying these in an interview costs you the question

  • Believing the variable names are matched to property names
  • Thinking destructuring works on any object automatically
  • Confusing it with array spread/`*` operator
  • Claiming it uses reflection at runtime

context

open as a page

How does `for ((key, value) in map)` work, and what enables destructuring a `Map.Entry` and lists/arrays?

level: middleimportance: should knowfreq 60%

basics

~10 s

Iterating a map yields Map.Entry objects, and each entry can be unpacked into key and value because Map.Entry provides component1() (key) and component2() (value). Lists and arrays provide component1()..component5() too.

open as a page

How do you skip components you don't need in a destructuring declaration, and what does the underscore actually do?

level: middleimportance: should knowfreq 55%

basics

~10 s

Use an underscore in the position you want to ignore. It tells the compiler to skip that component so the corresponding componentN() is never called and no variable is created.

open as a page

How do you make your own non-data class destructurable, and why might you deliberately add a `componentN()` operator?

level: seniorimportance: should knowfreq 35%

basics

~10 s

Declare operator fun componentN() functions on the class — one per position you want to expose. They can return anything, including computed values, not just constructor properties.

open as a page

How does destructuring work in lambda parameters, and what are the gotchas around the single-vs-multiple-parameter distinction?

level: seniorimportance: nice to knowfreq 30%

basics

~10 s

A lambda parameter wrapped in parentheses is destructured into its components instead of received whole. { (k, v) -> ... } takes ONE entry parameter and unpacks it, not two separate parameters.

open as a page