Sitemap

Think all Kotlin classes are created equal? Think again.

Kotlin Data Class vs Sealed Class: What You Really Need to Know

Stop choosing blindly — learn when to use data classes, when sealed classes shine, and how to avoid subtle pitfalls that trip up even seasoned developers.

3 min readJul 29, 2025

--

Press enter or click to view image in full size
Photo by Steppeland - Lutgarde De Brouwer on Unsplash

Not a Medium Member? “Read for Free”

In the Kotlin ecosystem, it’s easy to default to data class when modeling structured data, or reach for sealed class when dealing with state. But what if I told you these two aren't interchangeable—and choosing the wrong one can quietly sabotage your architecture?

In this article, we’ll go beyond surface-level definitions and explore the real differences between Kotlin’s data class and sealed class. You'll walk away with clarity on when to use what, and maybe even re-evaluate a few patterns in your current codebase.

Data Class: Kotlin’s Workhorse for Immutable Data

data class is perfect for representing plain, immutable data with automatic generation of:

  • equals(), hashCode()
  • toString()
  • copy()
  • componentN() functions (for destructuring)
data class User(val name: String, val age: Int)

Ideal for: DTOs, model layers, caching objects, serialization.

Sealed Class: The Power of Exhaustive State

A sealed class is a closed type hierarchy, meaning all its subclasses are known at compile time. It’s Kotlin’s way of enabling smart exhaustive when expressions without default cases.

Example:

sealed class NetworkState {
object Loading : NetworkState()
data class Success(val data: String) : NetworkState()
data class Error(val error: Throwable) : NetworkState()
}

Ideal for: UI states, error handling, modeling finite state machines.

Use a data class when:

  • You’re passing simple data between layers
  • You need immutability with convenience methods (copy, equals, etc.)
  • You’re working with serialization libraries like Moshi or Kotlinx Serialization

Use a sealed class when:

  • You need to represent known finite states (Success , Error , Loading)
  • You want the compiler to enforce when clause completeness
  • You’re modeling logic that evolves based on a specific set of outcomes

Common Mistakes Developers Make

1). Using data class to represent states

data class Loading(val showSpinner: Boolean)
data class Success(val data: List<Item>)
data class Error(val message: String)

This looks fine — until you realize there’s no enforced hierarchy. There’s nothing preventing you from mixing them up, and your when statements aren’t exhaustive. Bugs creep in silently.

Better: Use a sealed class for state.

2). Making a sealed class where a data class would suffice

Overengineering alert. If you’re just wrapping simple values (e.g., user data), adding sealed hierarchy overhead adds no value — just verbosity and confusion.

Conclusion

In the Kotlin world, choosing the right abstraction is like choosing the right tool in a toolbox — it can make your development journey smooth or needlessly complicated.

Next time you reach for data class out of habit, pause and ask: Am I just modeling data, or am I modeling state?

If you enjoyed this breakdown, consider following me for more Kotlin tips and clean architecture deep dives. Happy coding!

--

--

Jayant Kumar🇮🇳
Jayant Kumar🇮🇳

Written by Jayant Kumar🇮🇳

Jayant Kumar is a Lead Software Engineer, passionate about building modern Android applications. He shares his expertise in Kotlin, Android, and Compose.

No responses yet