Objects combine state and behavior
A class defines a type; calling its constructor creates an instance. Put data that belongs to one object in instance properties, and expose operations that preserve its rules.
class StudySession(val topic: String, val plannedMinutes: Int) {
init { require(plannedMinutes > 0) }
var completedMinutes: Int = 0
private set
fun study(minutes: Int) {
require(minutes > 0 && minutes <= plannedMinutes - completedMinutes)
completedMinutes += minutes
}
}
fun main() {
val session = StudySession("Kotlin", 25)
session.study(10)
check(session.completedMinutes == 10)
}
The private setter prevents callers from bypassing study. Two sessions have independent counters. An ordinary class does not automatically compare all properties for equality; choose a data class when value semantics are the intent.
Exercise
Add an isComplete computed property. Try studying zero minutes, exceeding the remaining budget, and completing the session exactly.
Check: invalid operations must leave the previous state intact. Explain why public mutable counters make that guarantee harder to maintain.