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 HashSet or used as HashMap keys 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.