Skip to main content
Kotlin Basics
CHAPTER 17 Beginner

Data Classes and Sealed Classes

Updated: May 18, 2026
5 min read

# CHAPTER 17

Data Classes and Sealed Classes

1. Chapter Introduction

In Java, if you want a class just to hold data (like a User with an ID and Name), you have to write the class, variables, getters, setters, a .toString() method, an .equals() method, and a .hashCode() method. It takes 50 lines of code just to hold a name! Kotlin solves this instantly with Data Classes. In this chapter, we will learn how Data Classes eliminate boilerplate, and we will explore Sealed Classes, which act as advanced Enums for modeling specific application states.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Declare and use a data class.
  • Understand the automatically generated methods (toString, equals, copy).
  • Destructure objects using component functions.
  • Define a sealed class.
  • Use sealed classes with the when expression for state modeling.

3. Data Classes

To create a class whose primary purpose is holding data, just add the data keyword before class.
kotlin
12
// That's it! 1 line of code.
data class User(val name: String, val age: Int)

By adding the data keyword, the compiler automatically generates:

  1. 1. toString() (Prints nicely formatted data instead of memory addresses).
  1. 2. equals() / == (Compares the actual data, not the memory reference).
  1. 3. hashCode()
  1. 4. copy() (To easily duplicate and alter immutable objects).
  1. 5. componentN() functions (For destructuring).

kotlin
12345678910
fun main() {
    val u1 = User("Alice", 25)
    val u2 = User("Alice", 25)

    // toString() is generated!
    println(u1) // Output: User(name=Alice, age=25)

    // equals() checks structural data!
    println(u1 == u2) // Output: true
}

4. The copy() Method

Because data classes heavily encourage using immutable val properties, you cannot easily change a user's age. Instead, you create a *copy* of the user, altering only the property you want to change!
kotlin
12345678
fun main() {
    val user1 = User("Bob", 30)
    
    // Creates a copy of Bob, but changes the age to 31
    val user2 = user1.copy(age = 31)
    
    println(user2) // Output: User(name=Bob, age=31)
}

5. Destructuring Declarations

Data classes generate component functions that allow you to unpack an object into multiple variables instantly.
kotlin
12345678
fun main() {
    val user = User("Charlie", 40)
    
    // Destructuring!
    val (extractedName, extractedAge) = user
    
    println("Extracted: $extractedName is $extractedAge")
}

6. Sealed Classes (Advanced State Modeling)

Enums are great for simple lists (like MONDAY, TUESDAY). But what if you are building an Android app and making a network request? The state is either Loading, Success (which contains data), or Error (which contains a message). Enums cannot hold different types of data!

Sealed Classes are Enums on steroids. A sealed class restricts inheritance: all subclasses MUST be defined in the same file. This allows the compiler to know exactly how many states exist.

kotlin
1234567891011121314151617181920
// Define the restricted hierarchy
sealed class NetworkResult {
    object Loading : NetworkResult() // Object (Singleton) because Loading has no data
    data class Success(val data: String) : NetworkResult() // Holds data
    data class Error(val exception: String) : NetworkResult() // Holds an error
}

fun processResult(result: NetworkResult) {
    // The compiler forces us to handle ALL sealed class subclasses in a 'when' expression!
    when (result) {
        is NetworkResult.Loading -> println("Show loading spinner...")
        is NetworkResult.Success -> println("Display data: ${result.data}")
        is NetworkResult.Error -> println("Show alert: ${result.exception}")
    }
}

fun main() {
    val response = NetworkResult.Success("Weather is 72 Degrees")
    processResult(response)
}

7. Common Mistakes

  • Empty Data Classes: A data class MUST have at least one parameter in its primary constructor. data class Empty() is an error.
  • Using var in Data Classes: While allowed, it is terrible practice. Data classes should represent a snapshot of state. Always use val. If state changes, use .copy().

8. Best Practices

  • Exhaustive when: Always use when as an expression (assigning it to a variable or using return when) when dealing with Sealed Classes. This forces the compiler to error out if you add a new subclass to the Sealed Class but forget to handle it in the when block!

9. Exercises

  1. 1. Create a data class Product(val id: Int, val name: String, val price: Double).
  1. 2. Instantiate Product(1, "Laptop", 999.99).
  1. 3. Print the object.
  1. 4. Use .copy() to create a new product with the same ID and Name, but a price of 899.99.

10. MCQs with Answers

Question 1

What keyword creates a class that automatically generates toString() and equals()?

Question 2

In a data class, what does the == operator evaluate?

Question 3

What generated method allows you to easily duplicate an immutable data class while altering specific properties?

Question 4

What feature allows val (name, age) = user?

Q5. Can a data class have an empty primary constructor? a) Yes b) No, it must have at least one parameter Answer: b) No.
Question 6

What is a Sealed Class?

Question 7

Why are Sealed Classes superior to Enums for state modeling?

Q8. When using a when expression with a Sealed Class, what massive benefit does the compiler provide? a) It runs concurrently b) Exhaustive checking: The compiler ensures you have handled every possible subclass, preventing unhandled state bugs. Answer: b) Exhaustive checking.
Question 9

Should data class parameters be val or var?

Q10. What does object Loading : NetworkResult() mean inside a sealed class? a) It creates a Singleton (an object that only exists once in memory) to represent a state with no data. b) It creates an interface Answer: a) It creates a Singleton object.

11. Interview Questions

  • Q: Explain what the Kotlin compiler generates behind the scenes when you use the data keyword.
  • Q: Explain the difference between an Enum and a Sealed Class. Give an example of when you would use a Sealed Class in Android Development.

12. Summary

Data Classes eliminate the most tedious part of Java development: writing boilerplate code just to hold state. Sealed Classes elevate state modeling, providing a safe, strictly-typed way to handle complex application states (like Network Responses) while forcing exhaustive compile-time checking. Together, they represent modern Kotlin's focus on expressiveness and safety.

13. Next Chapter Recommendation

Functions in Kotlin are first-class citizens. They are not just blocks of code; they can be passed around like variables! In Chapter 18: Extension Functions and Higher-Order Functions, we will unlock the true power of functional programming in Kotlin.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·