Combine strong identifiers and small extensions
Build a learning-session model with validated value types, a computed display label, and immutable update operations. Use advanced features only where they enforce a useful rule.
@JvmInline
value class TopicId(val value: String) {
init { require(value.matches(Regex("[a-z0-9-]+"))) }
}
data class Session(val topic: TopicId, val minutes: Int) {
init { require(minutes > 0) }
}
fun Session.label(): String = "${topic.value}: $minutes min"
The ID rule is stricter than nonblank validation and suitable for this exercise's restricted identifiers. Duration validation belongs in the model, so copy(minutes = 0) also fails construction rather than bypassing the rule.
Acceptance checks
Test valid IDs, spaces, uppercase input, zero minutes, and immutable copying to a different duration. Confirm the original session is unchanged after a valid copy.
Extension: add an operator only if its meaning is obvious, such as summing a dedicated duration type. Explain why adding a custom delegate or reflection solely to demonstrate it would make this model harder to maintain.