skip to content

copy() & Immutable Updates

copy() produces a new instance with selected properties replaced, which is how you evolve immutable state. Interviewers connect it to state management, and to the fact that copy is shallow, so nested mutable structures are still shared.

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

questions

5

What does the generated copy() function on a Kotlin data class do, and why is it useful for immutable data?

level: juniorimportance: must knowfreq 80%

answer

  1. Compiler-generated for data classes
  2. Params mirror primary constructor, default to current value
  3. Non-destructive update of val properties
  4. Shallow copy — references shared
  5. Original stays unchanged

basics

~10 s

copy() makes a new object that is the same as the old one, except for the properties you choose to change. The original stays untouched, so you update data without modifying it in place.

solid answer

~40 s

For every data class, the Kotlin compiler generates a copy() function whose parameters mirror the primary-constructor properties, each defaulting to the current instance's value. Calling copy(name = "X") returns a brand-new instance with name replaced and all other properties copied across unchanged. Because data classes typically declare val properties, you cannot mutate state in place; copy() is the idiomatic non-destructive update. It only covers properties declared in the primary constructor, performs a shallow copy (referenced objects are shared, not duplicated), and pairs naturally with immutable design: keep objects read-only and produce new versions for each state transition. This makes state changes explicit, thread-friendlier, and easy to reason about.

code

kotlin · 6 lines
kotlin
data class User(val name: String, val age: Int)

val u1 = User("Ada", 30)
val u2 = u1.copy(age = 31)
println(u1) // User(name=Ada, age=30)
println(u2) // User(name=Ada, age=31)

go deeper

for a junior

Knows copy() returns a new object changing only named properties and leaves the original intact.

for a middle

Adds that defaults come from the current instance and only primary-constructor props are covered.

for a senior

Frames copy() as the idiomatic non-destructive update for val-based immutable design and notes shallow-copy semantics.

for a principal

Positions copy() within an immutability strategy and its limits at scale (shallow copy, nested updates, boilerplate).

## What copy() is A **data class** in Kotlin is a class declared with the `data` keyword whose main job is to hold data. For such a class the compiler auto-generates several members, one of which is `copy()`. `copy()` lets you create a **new instance** based on an existing one, changing only the properties you specify and keeping the rest identical. ## Why it exists Data classes usually declare their properties as `val` (read-only). A `val` cannot be reassigned after construction, so you cannot do `user.name = "new"`. Instead of mutating, you create a *new* object — this is a **non-destructive update**. `copy()` makes that ergonomic. ## How the signature is generated For each property in the **primary constructor**, `copy()` gets a parameter with the **same name** and a **default value equal to the current instance's value**. So you only pass the ones you want to change. ```kotlin data class User(val name: String, val age: Int) val u1 = User("Ada", 30) val u2 = u1.copy(age = 31) // User(name=Ada, age=31) // u1 is unchanged: User(name=Ada, age=30) ``` ## Key facts - **Only primary-constructor properties** participate. Properties declared in the class body are NOT copied via parameters. - It is a **shallow copy**: if a property holds a reference (e.g., a `List` or another object), the new instance shares that same reference; the referenced object is not duplicated. - The original instance is never modified. - Use **named arguments** (`copy(age = 31)`) for clarity, since positional copies are error-prone. ## Typical use ```kotlin fun birthday(user: User): User = user.copy(age = user.age + 1) ``` This returns a new `User`, leaving the input untouched — the foundation of immutable state handling.

  • Does copy() modify the original object?
    No. It returns a new instance; the original is left exactly as it was.
  • What value do copy() parameters default to?
    Each parameter defaults to the corresponding property's value on the instance you call copy() on.

Like photocopying a form and editing one field on the copy — the original sheet is untouched.

saying these in an interview costs you the question

  • Saying copy() mutates the original in place
  • Claiming it deep-copies nested objects
  • Thinking it copies properties declared in the class body
  • Believing copy() is available on every class, not just data classes

context

open as a page

copy() performs a shallow copy. What problems can this cause when a data class holds mutable or nested objects, and how do you update nested immutable state correctly?

level: middleimportance: must knowfreq 70%

basics

~20 s

copy() only copies the top object. Anything it points to (lists, other objects) is shared with the original. If that shared thing is mutable, changing it affects both. To update nested data, copy() the inner object too and slot it in.

open as a page

Why might copy() silently fail to carry over a property, and what is the relationship between copy() and the primary constructor / equals / hashCode?

level: middleimportance: should knowfreq 55%

basics

~20 s

copy() only knows about properties listed in the main constructor. A property set inside the class body is recreated from scratch on copy and may be lost or reset. The same constructor-only rule applies to equals, hashCode, and toString.

open as a page

Show how copy() underpins immutable state transitions in a reducer-style update. Why is this preferable to in-place mutation, especially for concurrency and frameworks like Jetpack Compose / StateFlow?

level: seniorimportance: should knowfreq 60%

basics

~20 s

You model state as an immutable data class and produce each new state with copy(). Because every state is a distinct, unchanging object, comparing old vs new is reliable, change detection works, and concurrent readers never see a half-updated object.

open as a page

What are the design limitations of relying on data class copy() for immutable updates at scale, and what alternatives or mitigations would you consider?

level: principalimportance: nice to knowfreq 35%

basics

~20 s

copy() is great for small, flat objects but gets painful for deep or large state: lots of nested copy() calls, only-public-constructor updates, shallow copying, and allocation cost. For big trees, use lens libraries, restructure state, or normalize it.

open as a page