Popular Searches
Popular Course Categories
Popular Courses

Using Data Classes, Enum Classes, and Sealed Classes

Using Data Classes, Enum Classes, and Sealed Classes

Object-Oriented Kotlin and Advanced Language Features

Using Data Classes, Enum Classes, and Sealed Classes in Kotlin

Course: Android App Development

Topic: Using Data Classes, Enum Classes, and Sealed Classes

Course Details: Android App Development Training

Demo Registration: Register for a Course Demo

JustAcademy Homepage: Visit JustAcademy


1. Introduction

Kotlin provides specialized class types that help developers build organized, maintainable, and type-safe Android applications. Three important types are data classes, enum classes, and sealed classes.

A data class represents structured information. An enum class defines a fixed set of named constants. A sealed class represents a restricted family of related types, making it easier to handle different outcomes and states safely.

These features are useful when developing Android applications that manage user profiles, product catalogs, payment methods, API responses, loading indicators, and error messages.

Learning Objectives

  • Understand the purpose and syntax of Kotlin data classes.
  • Create objects and use generated data-class functions.
  • Compare objects using structural equality.
  • Use copy() and destructuring declarations.
  • Create enum classes with constants, properties, and functions.
  • Use enum values in exhaustive when expressions.
  • Understand sealed classes and sealed interfaces.
  • Represent Android UI states and API outcomes with sealed types.
  • Choose the right class type for different programming requirements.

2. Understanding Data Classes

A data class is a special Kotlin class designed primarily to hold data. Kotlin generates several useful functions automatically, reducing the amount of repetitive code a developer needs to write.

For example, an Android learning application might need to store a student's ID, name, email address, and course name. A data class can represent all these values in a single object.

2.1 Data Class Syntax

data class Student(
    val id: Int,
    val name: String,
    val email: String
)

The data keyword declares a data class. Every primary-constructor parameter must use val or var, and at least one property must be declared in the primary constructor.

2.2 Creating Data Class Objects

data class Student(
    val id: Int,
    val name: String,
    val course: String
)

fun main() {
    val student1 = Student(
        101,
        "Aarav",
        "Android Development"
    )

    val student2 = Student(
        102,
        "Meera",
        "Kotlin Programming"
    )

    println(student1)
    println(student2)
}

Output

Student(id=101, name=Aarav, course=Android Development)
Student(id=102, name=Meera, course=Kotlin Programming)

Each object stores its own property values. Kotlin automatically generates a readable string representation of the primary-constructor properties.

3. Important Features of Data Classes

Kotlin generates several functions for data classes based on the properties declared in the primary constructor.

Function Purpose Example
toString() Creates a readable representation of the object. println(student)
equals() Compares relevant property values. student1 == student2
hashCode() Produces a hash code consistent with equality. student.hashCode()
copy() Creates a shallow copy with optional property changes. student.copy(name = "Riya")
componentN() Supports destructuring properties in declaration order. val (id, name) = student

4. Using the toString() Function

The toString() function returns a readable representation of a data-class object. It is useful for debugging and inspecting values during development.

data class Product(
    val name: String,
    val price: Double,
    val inStock: Boolean
)

fun main() {
    val product = Product(
        "Wireless Mouse",
        799.0,
        true
    )

    println(product.toString())
}

Output

Product(name=Wireless Mouse, price=799.0, inStock=true)

The output includes the class name and primary-constructor properties. Avoid logging sensitive information such as passwords, authentication tokens, or private customer data in production applications.

5. Comparing Data Class Objects

Data classes generate an equals() implementation based on their primary-constructor properties. The == operator checks structural equality, meaning it compares the relevant values rather than requiring both references to point to the same object.

data class User(
    val id: Int,
    val name: String
)

fun main() {
    val user1 = User(1, "Riya")
    val user2 = User(1, "Riya")
    val user3 = User(2, "Karan")

    println(user1 == user2)
    println(user1 == user3)
}

Output

true
false

The first two objects contain equal primary-constructor property values. The third object has different values, so it does not compare as equal.

Structural Equality vs. Referential Equality

val first = User(1, "Riya")
val second = User(1, "Riya")

println(first == second)   // true
println(first === second)  // false

The == operator checks structural equality, while === checks whether two references point to the exact same object.

6. Copying Objects with copy()

The copy() function creates a new data-class instance using the original object's property values as defaults. You can override selected properties without specifying every value again.

Example

data class Employee(
    val id: Int,
    val name: String,
    val salary: Double
)

fun main() {
    val employee1 = Employee(
        101,
        "Amit",
        40000.0
    )

    val employee2 = employee1.copy(
        salary = 45000.0
    )

    println(employee1)
    println(employee2)
}

Output

Employee(id=101, name=Amit, salary=40000.0)
Employee(id=101, name=Amit, salary=45000.0)

The original employee remains unchanged. The copied object retains the same ID and name but contains an updated salary.

6.1 Understanding Shallow Copying

The generated copy() function performs a shallow copy. If a property refers to a mutable object, the original and copied objects may share that same nested object.

data class Team(
    val name: String,
    val members: MutableList<String>
)

fun main() {
    val original = Team(
        "Android Team",
        mutableListOf("Aarav")
    )

    val copied = original.copy()

    copied.members.add("Meera")

    println(original.members)
    println(copied.members)
}

Output

[Aarav, Meera]
[Aarav, Meera]

Both objects display the added member because they reference the same mutable list. To create independent nested data, explicitly copy the list or use an appropriate immutable collection.

7. Destructuring Data Classes

Destructuring allows you to extract properties into separate variables. Kotlin generates component functions for primary-constructor properties, allowing them to be accessed in declaration order.

data class Course(
    val title: String,
    val duration: Int,
    val fee: Double
)

fun main() {
    val course = Course(
        "Android Development",
        12,
        15000.0
    )

    val (title, duration, fee) = course

    println(title)
    println(duration)
    println(fee)
}

Output

Android Development
12
15000.0

Destructuring follows the order of properties in the primary constructor. For complex objects, accessing properties by name may be clearer than relying on their positions.

8. Properties Outside the Primary Constructor

Only properties declared in the primary constructor participate in the generated equals(), hashCode(), toString(), copy(), and component functions.

data class Person(
    val name: String
) {
    var age: Int = 0
}

fun main() {
    val person1 = Person("Aarav")
    val person2 = Person("Aarav")

    person1.age = 20
    person2.age = 30

    println(person1 == person2)
}

Output

true

The objects compare as equal because their primary-constructor property, name, has the same value. Their age properties are not included in generated equality.

9. Data Classes in Android Development

Data classes are commonly used for user profiles, product information, course details, application settings, and data received from APIs.

Example: Course Model

data class CourseResponse(
    val id: Int,
    val title: String,
    val durationWeeks: Int,
    val fee: Double
)

fun main() {
    val course = CourseResponse(
        id = 201,
        title = "Android App Development",
        durationWeeks = 12,
        fee = 18000.0
    )

    println("Course: ${course.title}")
    println("Duration: ${course.durationWeeks} weeks")
}

In an Android application, a network layer can convert a compatible API response into a data model using a serialization library. The resulting object can then be passed to a repository, ViewModel, or UI component.

Data Class Best Practices

  • Use meaningful class and property names.
  • Prefer val for values that should not be reassigned.
  • Keep data models focused on representing data.
  • Place properties that define value equality in the primary constructor when appropriate.
  • Remember that copy() does not recursively copy nested objects.
  • Validate important business rules at suitable application or domain boundaries.
  • Avoid storing sensitive information in objects that may be logged.

10. Understanding Enum Classes

An enum class represents a fixed set of named constants. It is useful when a value should be selected from a known list of options.

For example, an online shopping application may represent order states as pending, processing, shipped, or delivered. An enum class makes these states explicit and prevents arbitrary strings from being used accidentally.

10.1 Enum Class Syntax

enum class OrderStatus {
    PENDING,
    PROCESSING,
    SHIPPED,
    DELIVERED
}

10.2 Accessing Enum Constants

enum class PaymentStatus {
    PENDING,
    SUCCESS,
    FAILED
}

fun main() {
    val status = PaymentStatus.SUCCESS

    println(status)
    println(status.name)
}

Output

SUCCESS
SUCCESS

Enum constants are accessed using the enum class name, such as PaymentStatus.SUCCESS. This helps ensure that the selected value belongs to the declared set of options.

11. Using Enum Classes with when

Kotlin's when expression can execute different logic depending on an enum constant. If all enum entries are handled, Kotlin can verify that the expression is exhaustive without requiring an else branch.

enum class OrderStatus {
    PENDING,
    PROCESSING,
    SHIPPED,
    DELIVERED
}

fun getStatusMessage(status: OrderStatus): String {
    return when (status) {
        OrderStatus.PENDING ->
            "Your order is waiting for confirmation"

        OrderStatus.PROCESSING ->
            "Your order is being prepared"

        OrderStatus.SHIPPED ->
            "Your order is on the way"

        OrderStatus.DELIVERED ->
            "Your order has been delivered"
    }
}

fun main() {
    println(getStatusMessage(OrderStatus.SHIPPED))
}

Output

Your order is on the way

If a new enum constant is added later, the compiler can identify exhaustive when expressions that need to handle the new entry.

12. Enum Classes with Properties and Constructors

Enum constants can hold additional information. An enum class may declare constructor parameters, properties, and functions that apply to its constants.

enum class Priority(
    val level: Int,
    val label: String
) {
    LOW(1, "Low Priority"),
    MEDIUM(2, "Medium Priority"),
    HIGH(3, "High Priority")
}

fun main() {
    val priority = Priority.HIGH

    println(priority.level)
    println(priority.label)
}

Output

3
High Priority

This approach is useful when each predefined option needs a numeric level, display label, or other fixed metadata.

Adding Functions to Enum Classes

enum class ConnectionStatus(
    val description: String
) {
    CONNECTED("Connected to server"),
    DISCONNECTED("Not connected"),
    CONNECTING("Connection in progress");

    fun isConnected(): Boolean {
        return this == CONNECTED
    }
}

fun main() {
    println(ConnectionStatus.CONNECTED.description)
    println(ConnectionStatus.CONNECTED.isConnected())
    println(ConnectionStatus.DISCONNECTED.isConnected())
}

Output

Connected to server
true
false

The semicolon separates the enum constants from the additional class members. The function checks whether the current enum value is CONNECTED.

13. Useful Enum Properties and Functions

Property or Function Description Example
name Returns the declared name of a constant. Priority.HIGH.name
ordinal Returns the zero-based declaration position. Priority.HIGH.ordinal
entries Provides the enum entries as a list on supported Kotlin versions. Priority.entries
valueOf() Finds an entry by its exact declared name or throws an exception. Priority.valueOf("HIGH")
firstOrNull() Can find the first entry that matches a condition, or return null. Priority.entries.firstOrNull { it.level == 2 }

Avoid using ordinal as a persistent database or API identifier because changing the declaration order changes ordinal values. Prefer explicit stable codes when values must remain consistent across application versions.

Safe Enum Lookup

enum class ThemeMode {
    LIGHT,
    DARK,
    SYSTEM
}

fun parseThemeMode(value: String): ThemeMode? {
    return ThemeMode.entries.firstOrNull {
        it.name.equals(value, ignoreCase = true)
    }
}

fun main() {
    println(parseThemeMode("dark"))
    println(parseThemeMode("unknown"))
}

Output

DARK
null

This approach handles unknown input without throwing an exception. The entries property is available in modern Kotlin versions; older projects may need an alternative supported API.

14. Enum Classes in Android Applications

Enum classes can represent predefined choices such as theme modes, sort orders, connection states, or small sets of local workflow states.

enum class SortOrder {
    NAME_ASCENDING,
    NAME_DESCENDING,
    PRICE_LOW_TO_HIGH,
    PRICE_HIGH_TO_LOW
}

fun getSortLabel(order: SortOrder): String {
    return when (order) {
        SortOrder.NAME_ASCENDING -> "Name: A to Z"
        SortOrder.NAME_DESCENDING -> "Name: Z to A"
        SortOrder.PRICE_LOW_TO_HIGH -> "Price: Low to High"
        SortOrder.PRICE_HIGH_TO_LOW -> "Price: High to Low"
    }
}

fun main() {
    println(getSortLabel(SortOrder.PRICE_LOW_TO_HIGH))
}

Output

Price: Low to High

For values exchanged with external APIs or saved in a database, explicitly map enum entries to stable identifiers instead of depending on their ordinal positions or assuming their names will never change.

15. Understanding Sealed Classes

A sealed class defines a restricted family of subclasses. Kotlin's sealed inheritance rules allow the compiler to reason about the known direct subclasses, making sealed classes useful for representing a controlled set of alternatives.

For example, a network request might be loading, successful with data, or failed with an error message. A sealed class can model each state as a separate variant.

15.1 Basic Syntax

sealed class ResultState {
    data object Loading : ResultState()

    data class Success(
        val message: String
    ) : ResultState()

    data class Error(
        val reason: String
    ) : ResultState()
}

The hierarchy contains three variants. Loading is a singleton object, while Success and Error hold additional data.

The data object declaration is supported in modern Kotlin versions. For older Kotlin versions, a regular object can be used where appropriate.

15.2 Creating Sealed Class Values

fun main() {
    val loading = ResultState.Loading
    val success = ResultState.Success("Data loaded")
    val error = ResultState.Error("Network unavailable")

    println(loading)
    println(success)
    println(error)
}

You cannot instantiate a sealed class directly because it is abstract. Instead, create an instance of one of its permitted subclasses or use an object variant.

16. Handling Sealed Classes with when

The main advantage of a sealed hierarchy is that Kotlin can check whether all known variants are handled in an exhaustive when expression.

sealed class ApiResult {
    data object Loading : ApiResult()

    data class Success(
        val data: String
    ) : ApiResult()

    data class Error(
        val message: String
    ) : ApiResult()
}

fun renderResult(result: ApiResult): String {
    return when (result) {
        ApiResult.Loading ->
            "Please wait..."

        is ApiResult.Success ->
            "Result: ${result.data}"

        is ApiResult.Error ->
            "Error: ${result.message}"
    }
}

fun main() {
    println(renderResult(ApiResult.Loading))
    println(renderResult(ApiResult.Success("Profile loaded")))
    println(renderResult(ApiResult.Error("Request failed")))
}

Output

Please wait...
Result: Profile loaded
Error: Request failed

The is checks identify class-based variants carrying data. The singleton object variant is matched directly. Since every permitted variant is handled, no else branch is necessary here.

17. Modeling Android UI States with Sealed Classes

Android applications frequently need to represent loading, successful data retrieval, empty results, and errors. A sealed class can model these conditions explicitly instead of using multiple unrelated Boolean flags.

Example: Course Screen State

data class Course(
    val id: Int,
    val title: String
)

sealed class CourseUiState {
    data object Loading : CourseUiState()

    data class Success(
        val courses: List<Course>
    ) : CourseUiState()

    data object Empty : CourseUiState()

    data class Error(
        val message: String
    ) : CourseUiState()
}

fun describeState(state: CourseUiState): String {
    return when (state) {
        CourseUiState.Loading ->
            "Show loading indicator"

        is CourseUiState.Success ->
            "Display ${state.courses.size} courses"

        CourseUiState.Empty ->
            "Show empty state message"

        is CourseUiState.Error ->
            "Show error: ${state.message}"
    }
}

fun main() {
    val courses = listOf(
        Course(1, "Kotlin Basics"),
        Course(2, "Android UI Development")
    )

    println(describeState(CourseUiState.Success(courses)))
}

Output

Display 2 courses

In a real Android application, a ViewModel can expose this state to the UI. The UI then renders a loading indicator, course list, empty screen, or error message according to the current state. The rendering implementation differs between Jetpack Compose and the traditional Android View system.

18. Sealed Interfaces

A sealed interface restricts the set of its direct implementations. Unlike a class, an interface does not provide a constructor for implementing types to call. A class can also implement multiple interfaces.

sealed interface AppEvent {
    data class Login(
        val username: String
    ) : AppEvent

    data object Logout : AppEvent

    data class OpenCourse(
        val courseId: Int
    ) : AppEvent
}

fun handleEvent(event: AppEvent): String {
    return when (event) {
        is AppEvent.Login ->
            "Login requested for ${event.username}"

        AppEvent.Logout ->
            "Logout requested"

        is AppEvent.OpenCourse ->
            "Opening course ${event.courseId}"
    }
}

fun main() {
    println(handleEvent(AppEvent.Login("riya_dev")))
}

Output

Login requested for riya_dev

Sealed interfaces are useful for representing application events, capabilities, and result types when a controlled set of implementations is needed without requiring a single class inheritance path.

19. Combining Data Classes, Enum Classes, and Sealed Classes

These three class types can work together in the same application. A data class stores information, an enum class represents a fixed category, and a sealed class represents possible outcomes or states.

Example: Course Enrollment

data class Course(
    val id: Int,
    val title: String,
    val fee: Double
)

enum class EnrollmentStatus {
    PENDING,
    APPROVED,
    REJECTED
}

sealed class EnrollmentResult {
    data class Success(
        val course: Course,
        val status: EnrollmentStatus
    ) : EnrollmentResult()

    data class Failure(
        val message: String
    ) : EnrollmentResult()

    data object Loading : EnrollmentResult()
}

fun displayEnrollment(result: EnrollmentResult): String {
    return when (result) {
        EnrollmentResult.Loading ->
            "Processing enrollment..."

        is EnrollmentResult.Success ->
            "Course: ${result.course.title}, Status: ${result.status}"

        is EnrollmentResult.Failure ->
            "Enrollment failed: ${result.message}"
    }
}

fun main() {
    val course = Course(
        101,
        "Android App Development",
        18000.0
    )

    val result = EnrollmentResult.Success(
        course,
        EnrollmentStatus.APPROVED
    )

    println(displayEnrollment(result))
}

Output

Course: Android App Development, Status: APPROVED

How the Types Work Together

  • Data class: Course stores the course ID, title, and fee.
  • Enum class: EnrollmentStatus limits the status to predefined values.
  • Sealed class: EnrollmentResult represents loading, success, and failure.
  • When expression: The function handles every defined enrollment outcome.

20. Differences Between Data Classes, Enum Classes, and Sealed Classes

Feature Data Class Enum Class Sealed Class
Primary purpose Represent and compare structured data. Represent a fixed set of constants. Represent a restricted family of alternatives.
Instances Many instances with different data. A predefined set of enum constants. Instances of permitted subclasses, including singleton variants.
Associated data Yes, through properties. Yes, constants can carry fixed values. Yes, different subclasses can carry different data.
Generated functions Data-related functions are generated. Provides enum-specific properties and functions. No general data-class functions are generated for the hierarchy itself.
Typical Android use API models, user profiles, course records. Theme modes, sort orders, fixed statuses. Loading, success, failure, and application events.

21. Choosing the Right Class Type

Requirement Recommended Type Reason
Store a student's ID and name. Data class The object primarily represents information.
Represent LIGHT, DARK, and SYSTEM modes. Enum class The values are a fixed set with a similar basic structure.
Represent loading, success with data, and failure with a message. Sealed class Each alternative may carry different information.
Represent typed API outcomes. Sealed class or sealed interface It provides explicit alternatives and exhaustive handling.
Represent a product with custom business behavior. Regular class or data class The best choice depends on whether generated value-based behavior is appropriate.

22. Common Mistakes and Troubleshooting

Problem Cause Solution
Data class constructor error A primary-constructor parameter is missing val or var. Declare every primary-constructor parameter as a property.
Unexpected equality result A property declared in the class body is excluded from generated equality. Place properties that define value equality in the primary constructor when appropriate.
Original changes after copying The copy shares a mutable nested object with the original. Copy the nested object or use immutable collections.
Enum lookup throws an exception valueOf() receives an unknown or incorrectly cased name. Validate the input or use a safe lookup function.
Persisted enum values change meaning The application relies on enum ordinal positions. Use explicit stable codes for persistence and external interfaces.
Non-exhaustive when expression A permitted enum or sealed variant is not handled. Add branches for all applicable variants.
Cannot instantiate a sealed class A sealed class is abstract. Create an instance of one of its permitted subclasses.
Data object syntax is unsupported The project uses an older Kotlin version. Use a regular object when suitable or update the Kotlin toolchain.

23. Best Practices for Android Developers

  • Use data classes for API models and objects that primarily carry data.
  • Prefer read-only properties where practical.
  • Remember that data-class equality uses primary-constructor properties.
  • Remember that the generated copy() function is shallow.
  • Use enum classes for small, closed sets of similarly structured constants.
  • Avoid depending on enum ordinals for persistent identifiers.
  • Use sealed classes for outcomes or states that can contain different data.
  • Handle all known sealed variants in exhaustive when expressions.
  • Separate UI rendering from data models and business logic.
  • Test success, loading, empty, and failure states.
  • Use meaningful variant names such as Loading, Success, and Error.
  • Choose class types according to the needs of the application domain.

24. Practical Exercises

Exercise 1: Student Data Class

  • Create a data class named Student.
  • Add properties for ID, name, course, and email.
  • Create two objects and compare them using ==.
  • Use copy() to change one student's course.
  • Print the object and test destructuring.

Exercise 2: Order Status Enum

  • Create an enum class with PLACED, PACKED, SHIPPED, and DELIVERED.
  • Add a readable description for each constant.
  • Write a function that returns a message using a when expression.
  • Test every enum constant.

Exercise 3: Sealed API Result

  • Create a sealed class named ApiResult.
  • Add loading, success, and error variants.
  • Make the success variant hold a list of courses.
  • Make the error variant hold an error message.
  • Write an exhaustive when expression to process every result.

Exercise 4: Combine All Three Types

  • Create a data class named Course with ID, title, and fee.
  • Create an enum class for course difficulty: BEGINNER, INTERMEDIATE, and ADVANCED.
  • Create a sealed class representing loading, successful retrieval, and errors.
  • Write a function that displays the correct message for each state.

25. Mini Project: Course Catalog State Manager

Build a Kotlin program that represents a course catalog and its possible loading states. This project combines data classes, enum classes, sealed classes, and exhaustive when expressions.

data class Course(
    val id: Int,
    val title: String,
    val fee: Double
)

enum class CourseLevel {
    BEGINNER,
    INTERMEDIATE,
    ADVANCED
}

sealed class CatalogState {
    data object Loading : CatalogState()

    data class Success(
        val courses: List<Course>,
        val level: CourseLevel
    ) : CatalogState()

    data class Error(
        val message: String
    ) : CatalogState()
}

fun displayCatalog(state: CatalogState): String {
    return when (state) {
        CatalogState.Loading ->
            "Loading courses..."

        is CatalogState.Success ->
            "Loaded ${state.courses.size} ${state.level} courses"

        is CatalogState.Error ->
            "Unable to load courses: ${state.message}"
    }
}

fun main() {
    val courses = listOf(
        Course(1, "Kotlin Fundamentals", 5000.0),
        Course(2, "Android App Development", 18000.0)
    )

    val state = CatalogState.Success(
        courses = courses,
        level = CourseLevel.BEGINNER
    )

    println(displayCatalog(state))
}

Expected Output

Loaded 2 BEGINNER courses

Project Learning Outcomes

  • Represent course information with a data class.
  • Represent fixed difficulty levels with an enum class.
  • Represent catalog states with a sealed class.
  • Handle all known states with an exhaustive when expression.
  • Understand how the same design can be adapted to an Android screen backed by a ViewModel.

26. Interview Questions and Answers

Q1. What is a data class in Kotlin?

Answer: A data class is designed primarily to hold data. Kotlin generates functions such as equals(), hashCode(), toString(), copy(), and component functions based on its primary-constructor properties.

Q2. What are the requirements for declaring a data class?

Answer: It must have at least one primary-constructor parameter, and every primary-constructor parameter must be declared using val or var.

Q3. What is the difference between == and ===?

Answer: == checks structural equality through equals(), while === checks whether two references point to the same object.

Q4. What does copy() do?

Answer: It creates a new data-class instance using the original properties as defaults, with the option to change selected values. The generated copy is shallow.

Q5. What is an enum class?

Answer: An enum class defines a fixed set of named constants, such as payment statuses or application theme modes.

Q6. Can enum classes contain properties and functions?

Answer: Yes. Enum classes can define constructor properties, functions, and behavior for their constants.

Q7. What is a sealed class?

Answer: A sealed class defines a restricted hierarchy of subclasses and is useful when a value must represent one of a known set of alternatives.

Q8. Why are sealed classes useful with when expressions?

Answer: They allow the compiler to check whether all known variants are handled, reducing the risk of missing an important state.

Q9. What is the difference between enum and sealed classes?

Answer: An enum represents a fixed set of constants with a common basic structure. A sealed hierarchy can represent alternatives with different properties and behaviors.

Q10. What is a sealed interface?

Answer: It is an interface with a restricted set of direct implementations, useful when controlled alternatives are needed without requiring a single class inheritance path.

Q11. How can sealed classes help Android UI development?

Answer: They can represent loading, success, empty, and error states, allowing the UI to respond to each state explicitly.

Q12. Should enum ordinal values be stored in a database?

Answer: Usually not. Ordinals depend on declaration order. Use explicit stable codes or identifiers when values must persist across application versions.

27. Quick Revision Checklist

  • Understand data classes and their generated functions.
  • Know how to compare, copy, and destructure data-class objects.
  • Remember that properties outside the primary constructor do not affect generated equality.
  • Understand shallow copying and nested mutable objects.
  • Use enum classes for fixed sets of values.
  • Know how to use enum properties, functions, and when expressions.
  • Avoid relying on enum ordinal values for persistent identifiers.
  • Understand how sealed classes restrict a type hierarchy.
  • Use sealed types to represent UI states, API outcomes, and application events.
  • Handle all known variants in exhaustive when expressions.
  • Practice combining the three class types in a Kotlin project.

28. Conclusion

Data classes, enum classes, and sealed classes provide complementary ways to model information and behavior in Kotlin. Data classes simplify the representation and comparison of structured data. Enum classes make fixed choices explicit and type-safe. Sealed classes and interfaces represent restricted alternatives, enabling reliable handling of application states and outcomes.

In Android development, these features are useful for API models, user profiles, course catalogs, application settings, and UI state management. Practicing the examples and mini project in this lesson will help you write Kotlin code that is clearer, safer, and easier to maintain.

29. Additional Learning Resources

Previous Next
whatsapp