data, sealed, object, enum — when to use which
Kotlin's special class kinds are an interview favorite because each encodes a design intent:
- data class — a value carrier:
equals/hashCode/toString/copy/componentNare generated automatically (only over primary constructor properties). - sealed class / sealed interface — a restricted hierarchy: all subtypes are known at compile time, so
whenbecomes exhaustive. - object — the language's built-in singleton: thread-safe, lazily initialized.
- companion object — static-like members tied to a class, the typical home of factory methods.
- enum class — a fixed set of constants; each constant can hold state and override methods.
| Question | sealed class | enum class |
|---|---|---|
| Can each variant carry its own data? | Yes — each subclass with different properties | Limited — all constants share the same fields |
| Instance count | Any number per subtype | Exactly one per constant |
| Typical use | UI state, API results (Success/Error/Loading) | Fixed lists: currency, status codes |
Classic interview question: "How would you model an API response?" The expected answer is a sealed class: Success(data), Error(code, message), Loading. Because when is exhaustive, adding a new case makes the compiler flag every usage site — in a banking app this prevents forgotten error-handling branches.
🛠 Practice task
Model a payment outcome: a sealed interface PaymentResult with Success, Declined and NetworkError variants (each carrying different data).
- Write a handler with
whenand no `else` — the compiler should be satisfied. - Now add a
Pendingvariant and see where the compiler flags you. - Try building the same model as an
enum classand write down what becomes impossible.
Done when: you can answer "why sealed, not enum" using your own example.
📚 Sources and documentation
- Data classes — Kotlin docsofficialkotlinlang.org
- Sealed classes and interfacesofficialkotlinlang.org
- Object declarations and expressionsofficialkotlinlang.org