Data classes, enums, and sealed types
Data class: a value made from properties
data class UserUi(val id: Long, val name: String)
Kotlin generates equals, hashCode, toString, componentN, and copy from primary-constructor properties.
val renamed = UserUi(1, "Ada").copy(name = "Grace")
copy is shallow: nested mutable objects are shared unless copied separately.
Enum: fixed named constants of one shape
enum class SortOrder { NAME, DATE, SIZE }
Use an enum when every option has the same conceptual shape. Enums may have properties and functions.
Sealed type: fixed variants with different data
sealed interface Result<out T> {
data class Success<T>(val value: T) : Result<T>
data class Failure(val message: String) : Result<Nothing>
data object Loading : Result<Nothing>
}
Each variant may carry different data. An exhaustive when forces every state to be handled.
Expected: value, error, and loading descriptions.
Choosing the type
| Need | Choose |
|---|---|
| Store comparable values | Data class |
| Fixed constants with the same shape | Enum |
| Closed alternatives with different data | Sealed class/interface |
| One shared no-data state | data object |
✕ Contradictory Boolean state
data class UiState(val loading: Boolean, val error: Boolean, val data: String?)
// loading=true, error=true, data!=null is possible
✓ Model only valid alternatives
sealed interface UiState {
data object Loading : UiState
data class Content(val data: String) : UiState
data class Error(val message: String) : UiState
}
Interview-ready answer
Data classes model values, enums model a fixed set of same-shaped constants, and sealed types model a closed set of differently shaped states. Sealed types make when exhaustive, so adding a new state reveals every consumer that must handle it.