Understanding the difference between mutable and immutable can significantly impact your code's reliability, readability, and maintainability. Here's a deep dive into these concepts, with examples to illustrate why immutability is often the preferred choice.
Mutable vs Immutable: The Basics:
- Mutable: Refers to anything whose state or data can be changed after it is created. This can apply to variables, collections, and objects.
- Immutable: Refers to anything whose state or data cannot be changed once it is created. This immutability applies to variables, collections, and objects.
Example:
// mutable
var mutableList = mutableListOf("A", "B", "C")
mutableList.add("D")
println(immutableList) // Output: [A, B, C, D]
There are two mutable aspects here. First, the var
keyword allows the list to be reassigned to another value. Second, mutableListOf
allows the list to be modified after initialization.
// immutable
val immutableList = listOf("A", "B", "C")
// immutableList.add("D") // This will not compile
println(immutableList) // Output: [A, B, C]
Why Mutable is Bad?
1- Thread Safety Issues:
Mutable objects can lead to concurrent modification problems in a multi-threaded environment. This can cause unpredictable behavior and hard-to-find bugs.
var sharedList = mutableListOf<Int>()
// Thread 1
Thread {
for (i in 1..1000) {
sharedList.add(i)
}
}.start()
// Thread 2
Thread {
for (i in 1..1000) {
sharedList.add(i * 10)
}
}.start()
// Result may vary and can lead to data corruption
2- Unexpected Side Effects - Unpredictable Code:
Mutable objects can be changed from different parts of a program, leading to unexpected side effects and bugs that are difficult to trace.
fun modifyList(list: MutableList<String>) {
list.add("D")
}
val list = mutableListOf("A", "B", "C")
modifyList(list)
println(list) // Output: [A, B, C, D]
Since the list is mutable, its values can change unpredictably. To understand all the modifications made to the list, you must trace the code from its initialization to every point where it is used.
3- Ease of Testing:
Immutable objects simplify testing because their state cannot change. This eliminates side effects and makes it easier to write reliable unit tests.
fun addElementToList(list: List<String>, element: String): List<String> {
return list + element
}
val originalList = listOf("A", "B", "C")
val newList = addElementToList(originalList, "D")
assert(originalList == listOf("A", "B", "C"))
assert(newList == listOf("A", "B", "C", "D"))
Conclusion
In Kotlin, prefer using val
over var
and immutable collections like List
instead of MutableList
whenever possible. This simple practice can lead to significant improvements in your code quality.
While mutable objects have their place, especially in scenarios requiring frequent updates, immutability often leads to safer, more maintainable, and predictable code.
What's ahead: Understanding immutability sets the stage for the next things - Declarative code and Functional programming.
Top comments (1)
Nice post!