Data Class Immutable
ID |
kotlin.data_class_immutable |
Severity |
low |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Best Practice |
Language |
Kotlin |
Tags |
best-practice, reliability |
Description
Reports data class declarations that hold any mutable property (var), either
in the primary constructor or in the class body. A data class is meant to be a
plain value carrier — mutability breaks the contract that two instances with
the same fields are interchangeable.
Rationale
data class Point(var x: Int, var y: Int)
data class Tagged(val id: Int) {
var note: String = ""
}
Once a field can change after construction, several invariants that callers expect from a data class quietly break:
-
Instances stored in a
HashSetor used asHashMapkeys lose their bucket when a property is reassigned, because the recorded hash no longer matches the recomputed one. -
copy()returns a snapshot of the field values, so subsequent mutations of the original no longer propagate to the copy and vice versa, even though they share identity in callers' minds. -
Concurrent readers can see torn updates and intermediate states.
Remediation
data class Point(val x: Int, val y: Int)
// Update by returning a new instance instead of mutating in place.
val moved = point.copy(x = point.x + 1)
// If the field truly needs to change, use a normal class, not a data class.
class MutableTagged(val id: Int) {
var note: String = ""
}
Declare every data-class property as val. When a value needs to evolve,
return a new instance through copy(). If the type genuinely owns mutable
state, model it as a regular class rather than a data class.