Beginner: Kotlin · Classes, objects and collections · topic 10 of 50 · 30 min
Data, enum and sealed classes, and objects
Explanation
Data classes
Many classes just hold data: a Product, a Contact, an API response. For these, mark the class with data. Kotlin then writes useful functions for you, based on the properties in the primary constructor.
data class Product(val id: Int, val name: String, val price: Int)
fun main() {
val a = Product(1, "Kurta", 899)
val b = Product(1, "Kurta", 899)
println(a) // Product(id=1, name=Kurta, price=899)
println(a == b) // true: compares the data, not the memory address
val sale = a.copy(price = 699) // a new object with one change
val (id, name) = sale // destructuring
println("$id $name ${sale.price}") // 1 Kurta 699
}copy is important in Android: UI state is usually held in immutable data classes, and you create a changed version with copy instead of changing the original. That makes it easy for the UI to notice changes.
Only properties declared in the primary constructor take part in these generated functions. A data class must have at least one such property, and it cannot be open, abstract or sealed.
Enum classes
enum class PaymentMode(val label: String) {
UPI("UPI"), CARD("Card"), CASH("Cash on delivery");
fun isOnline() = this != CASH
}
fun main() {
val m = PaymentMode.UPI
println(m.label) // UPI
println(m.isOnline()) // true
println(PaymentMode.entries.size) // 3
}Sealed classes and interfaces
An enum constant cannot carry different data for each case. A sealed class (or sealed interface) is a closed family of types: all its subtypes are known to the compiler, and each can hold its own data. Combined with when, the compiler checks that you handled every case, so you do not need else.
sealed interface UiState {
data object Loading : UiState
data class Success(val items: List<String>) : UiState
data class Error(val message: String) : UiState
}
fun render(state: UiState): String = when (state) {
UiState.Loading -> "Showing spinner"
is UiState.Success -> "Showing ${state.items.size} items"
is UiState.Error -> "Error: ${state.message}"
} // no else needed: the compiler knows all casesIf someone later adds a new state, such as Empty, every when that forgot it stops compiling. That is a big safety win in large apps.
object and companion object
object declares a class and its single instance at the same time: a singleton. Use it for things there should be only one of, like app-wide constants. A companion object lives inside a class and holds members that belong to the class itself rather than to each object, similar to static members in Java; it is often used for factory functions and constants.
object AppConfig {
const val BASE_URL = "https://api.example.com/"
const val TIMEOUT_SECONDS = 30
}
class User private constructor(val phone: String) {
companion object {
fun fromInput(raw: String): User? {
val digits = raw.filter { it.isDigit() }.takeLast(10)
return if (digits.length == 10) User(digits) else null
}
}
}
fun main() {
println(AppConfig.BASE_URL)
println(User.fromInput("+91 98765 43210")?.phone) // 9876543210
}| Need | Use |
|---|---|
| Hold data, compare by value, copy with changes | data class |
| A fixed list of simple options | enum class |
| A fixed set of cases with different data | sealed class / sealed interface |
| Exactly one instance | object |
| Class-level helpers and constants | companion object |
Common mistakes
- Using an enum for states that need different data (Error with a message). Use a sealed interface.
- Adding else to a when over a sealed type: it hides missing cases. Leave it out so the compiler can help.
- Putting var properties in data classes used as UI state. Prefer val and copy.
- Using object for things that should have many instances.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Data, enum and sealed classes, and objects · 1/5
data class Product(val id: Int, val name: String)A data class holds data. Kotlin writes to string, equals, copy and destructuring for you.
Prefer a video by a teacher? Find free videos on “kotlin data class sealed class enum object companion” on YouTube ↗
In real life: Order status in a delivery app
A food delivery app models each order's status as a sealed interface: Placed, Preparing(minutesLeft), OutForDelivery(riderName, phone), Delivered, Cancelled(reason). The status screen uses one when, with no else. When the team adds "ReadyForPickup", Android Studio immediately shows every screen that must handle it.
sealed interface OrderStatus {
data object Placed : OrderStatus
data class Preparing(val minutesLeft: Int) : OrderStatus
data class OutForDelivery(val rider: String) : OrderStatus
data class Cancelled(val reason: String) : OrderStatus
}
fun message(s: OrderStatus) = when (s) {
OrderStatus.Placed -> "Order placed"
is OrderStatus.Preparing -> "Ready in ${s.minutesLeft} min"
is OrderStatus.OutForDelivery -> "${s.rider} is on the way"
is OrderStatus.Cancelled -> "Cancelled: ${s.reason}"
}
fun main() = println(message(OrderStatus.Preparing(12))) // Ready in 12 minPractice (0/3 done)
What does this print? data class P(val x: Int, val y: Int) fun main() { val a = P(1, 2) val b = a.copy(y = 5) println("$a $b ${a == b}") }
A screen can be Loading, show a list of items, or show an error message. Which type fits best?
Model a UPI payment: data class Payment(val id: String, val amountPaise: Long, val status: PayStatus) where PayStatus is a sealed interface with Pending, Success(val utr: String) and Failed(val reason: String). Write fun describe(p: Payment): String using when without else, and create a failed copy of a pending payment with copy.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Inheritance and interfacesClasses, objects and collections · same module
- Collections: List, Set and MapClasses, objects and collections · same module
- Dart 3: enums, records, patterns and sealed classesFlutter and Dart · Null safety and object-oriented Dart
- InterfacesJava Programming (Core to Spring Boot) · Inheritance, polymorphism and interfaces
- toString, equals, hashCode and recordsJava Programming (Core to Spring Boot) · Classes and objects
- Interview questions: KotlinAndroid career path
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What does == compare for two data class objects?
Answer: Their property values
Data classes generate equals based on properties.
Why can a when over a sealed type skip else?
Answer: The compiler knows every subtype and checks all are handled
Exhaustiveness is checked at compile time.
What is an object declaration?
Answer: A class with exactly one instance
object creates a singleton.
Which is like Java's static members?
Answer: companion object
Companion object members are accessed through the class name.
Where can subclasses of a sealed class be declared?Related
Answer: Only in the same library (file)
That is what makes the set known.
What does sealed interface Payment permits Upi, Card {} do?Related
Answer: Allows only Upi and Card to implement Payment
Sealed types restrict their implementations.
From Interfaces
Default Object.equals behaves like…Related
Answer: ==, same object only
Without overriding, equals checks identity.
When is a sealed interface better than an enum?Related
Answer: When cases carry different data
Subtypes can hold their own data.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 3 more topics in “Classes, objects and collections” before its exam: Inheritance and interfaces, Collections: List, Set and Map, Collection operations.
- Module 3 exam: Classes, objects and collections · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Coroutines and Flow. 50 topics left in the course before the final exam.