Data Classes & Sealed Classes
Use data classes for value-based equality and copy(), and sealed classes for exhaustive type hierarchies and clean state modeling.
Data Classes & Sealed Classes is a free Android Academy lesson on CoddyKit. This is lesson 3 of 4. Any 3 lessons of this learning path are free to read in full — after that, CoddyKit PRO unlocks every lesson, plus hands-on practice with a built-in code editor and a 24/7 AI tutor. It is part of the Android Academy learning path, and your progress syncs across the web and the CoddyKit app. The Android Academy course includes 4 lessons in total.
What Are Data Classes?
A data class in Kotlin is a class whose primary purpose is to hold data. The compiler automatically generates useful methods for you:
equals()— compare two instances by valuehashCode()— consistent hash based on propertiestoString()— readable string representationcopy()— create a modified copy- Destructuring support
Declaring a Data Class
Add the data keyword before class. All properties must be in the primary constructor:
data class User(val id: Int, val name: String, val email: String)
fun main() {
val u1 = User(1, "Alice", "alice@example.com")
val u2 = User(1, "Alice", "alice@example.com")
println(u1) // User(id=1, name=Alice, email=alice@example.com)
println(u1 == u2) // true (value equality)
println(u1 === u2) // false (different objects)
}copy() — Modify One Field
copy() creates a new instance with some properties changed. The original is not modified (immutability):
data class Product(val name: String, val price: Double, val inStock: Boolean)
fun main() {
val original = Product("Laptop", 999.0, true)
// Only change the price; keep everything else
val onSale = original.copy(price = 799.0)
println(original) // Product(name=Laptop, price=999.0, inStock=true)
println(onSale) // Product(name=Laptop, price=799.0, inStock=true)
}Destructuring Declarations
Data classes support destructuring — unpack properties into variables using componentN() functions generated by the compiler:
data class Point(val x: Int, val y: Int)
fun main() {
val point = Point(10, 20)
// Destructure into separate variables
val (x, y) = point
println("x=$x, y=$y") // x=10, y=20
// Also works in for loops with maps
val scores = mapOf("Alice" to 95, "Bob" to 88)
for ((name, score) in scores) {
println("$name: $score")
}
}What Are Sealed Classes?
A sealed class is a restricted class hierarchy — all subclasses must be defined in the same file. This makes the set of possible types known at compile time.
Use sealed classes to represent a finite set of states, like network results, UI states, or navigation events.
Sealed Class Syntax
Declare subclasses directly inside or in the same file:
sealed class NetworkResult<out T> {
data class Success<T>(val data: T) : NetworkResult<T>()
data class Error(val message: String, val code: Int = 0) : NetworkResult<Nothing>()
object Loading : NetworkResult<Nothing>()
}
fun handle(result: NetworkResult<String>) {
when (result) {
is NetworkResult.Success -> println("Data: ${result.data}")
is NetworkResult.Error -> println("Error ${result.code}: ${result.message}")
NetworkResult.Loading -> println("Loading...")
}
}Sealed Class + when (Exhaustive)
The power of sealed classes: when is exhaustive. The compiler forces you to handle every subtype — no forgotten cases:
sealed class UiState {
object Loading : UiState()
data class Success(val message: String) : UiState()
data class Error(val error: String) : UiState()
}
fun render(state: UiState): String = when (state) {
is UiState.Loading -> "Showing spinner"
is UiState.Success -> "Show: ${state.message}"
is UiState.Error -> "Error: ${state.error}"
// Compiler error if you remove any branch!
}
fun main() {
println(render(UiState.Loading))
println(render(UiState.Success("Loaded!")))
}Sealed Class vs Enum
Both represent a fixed set of values, but:
- Enum — each constant is a single instance, no extra data per variant
- Sealed class — each variant can carry different data, be a class or object
Use enum when variants need no data. Use sealed class when variants need different properties.
Object Declarations (Singletons)
object creates a singleton — one instance, created lazily, thread-safe:
object AppConfig {
const val BASE_URL = "https://api.example.com"
const val TIMEOUT = 30L
fun buildUrl(path: String) = "$BASE_URL/$path"
}
fun main() {
println(AppConfig.BASE_URL) // https://api.example.com
println(AppConfig.buildUrl("users")) // https://api.example.com/users
}Companion Objects
A companion object lives inside a class and provides class-level members (like Java's static). Commonly used for factory methods:
class Token private constructor(val value: String) {
companion object {
fun create(raw: String): Token {
val cleaned = raw.trim()
require(cleaned.isNotEmpty()) { "Token must not be blank" }
return Token(cleaned)
}
}
}
fun main() {
val token = Token.create(" abc123 ")
println(token.value) // abc123
}Data Classes in a Real App
Combine data classes with sealed classes for clean state modeling:
data class Post(val id: Int, val title: String, val body: String)
sealed class PostState {
object Loading : PostState()
data class Loaded(val posts: List<Post>) : PostState()
data class Failed(val reason: String) : PostState()
}
fun showState(state: PostState) = when (state) {
PostState.Loading -> println("Fetching posts...")
is PostState.Loaded -> println("${state.posts.size} posts loaded")
is PostState.Failed -> println("Failed: ${state.reason}")
}
fun main() {
showState(PostState.Loaded(listOf(
Post(1, "Hello", "World"),
Post(2, "Kotlin", "Rocks")
)))
}Quick Check
Which statement best describes what makes sealed classes different from regular abstract classes?
Recap: Data & Sealed Classes
You now have two powerful Kotlin tools:
- data class — value-based equality,
copy(), destructuring, auto-generatedtoString() - sealed class — finite type hierarchy, exhaustive
when, each variant can carry its own data - object — singleton, companion objects for factory methods
Together, these patterns replace Java boilerplate and enable clean, safe state modeling.
Learn Kotlin with an AI tutor — free
Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.
- Courses
- 36
- Lessons
- 152
Frequently Asked Questions
Is the “Data Classes & Sealed Classes” lesson free?
Yes — any 3 lessons of the Android Academy learning path, including “Data Classes & Sealed Classes”, are free to read in full here on the web. After that, CoddyKit PRO unlocks every lesson, plus interactive practice with a built-in code editor and a 24/7 AI tutor. The Android Academy course includes 4 lessons in total.
What will I learn in “Data Classes & Sealed Classes”?
Use data classes for value-based equality and copy(), and sealed classes for exhaustive type hierarchies and clean state modeling. You practise Android Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Android Academy?
No prior experience is required. Android Academy on CoddyKit is structured for beginners through advanced learners, so you can start here or from the beginning and move at your own pace. This is lesson 3 of 4.
How long does the “Data Classes & Sealed Classes” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Android Academy lesson?
Yes. Every Android Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Null Safety
- Classes & Objects
- Data Classes & Sealed Classes
- Extension Functions & Higher-Order Functions