Got a data class
? Great! They give us a magical copy()
method for free. Combine that with Kotlin's awesome scope functions like apply
or also
, and you've got a super-clean way to modify objects without actually modifying them. This means fewer bugs and safer concurrency!
Let's say we have a User
with a profile image.
data class User(
val id: String,
val name: String,
val email: String,
val profileImageUrl: String? = null, // Can be null
val isActive: Boolean = true
)
Now, imagine we need to update a user's profile image and perhaps deactivate them. Instead of directly changing properties (which is fine for var
but less safe), we can create a new User
instance with just the changes.
fun main() {
val initialUser = User(
id = "user123",
name = "Alice Wonderland",
email = "alice@example.com",
profileImageUrl = "http://old-image.com/alice.jpg"
)
println("Initial User: $initialUser")
// --- The Recipe ---
// 1. Update profile image and deactivate using copy() + apply
val updatedUser = initialUser.copy(
profileImageUrl = "http://new-image.com/alice_v2.jpg"
).apply {
// 'this' refers to the NEWLY CREATED user object
isActive = false // Note: 'isActive' would need to be 'var' for this direct assignment
}
// Better for val properties: use another copy() or constructor if needed
// For val properties, if you truly want to make multiple changes to the *new* object
// within a single flow, you might do something like this (more lines but fully immutable):
val fullyImmutableUpdatedUser = initialUser.copy(
profileImageUrl = "http://new-image.com/alice_v2.jpg"
).copy(isActive = false) // Chaining copy() calls for multiple immutable updates
println("Updated User: $fullyImmutableUpdatedUser")
println("Did initialUser change? ${initialUser.isActive} (No, it's still true!)")
// Let's activate someone else with a simpler update
val inactiveUser = User(id = "bob456", name = "Bob Builder", email = "bob@example.com", isActive = false)
val activatedUser = inactiveUser.copy(isActive = true)
println("Activated User: $activatedUser")
}
What happened here?
-
initialUser
remains unchanged thanks todata class
andcopy()
. -
copy()
creates a brand-newUser
object, letting us specify only the properties we want to change. - The original
initialUser
is stillisActive = true
, proving the immutability!
This pattern is fantastic for maintaining state in complex apps, especially when dealing with databases, network responses, or multi-threaded environments. Embrace immutability for cleaner, safer code!
Top comments (0)