Sitemap

Kotlin’s Evolution Just Made Your ā€˜class’ Obsolete

Why I Stopped Using class in Kotlin (And What I Use Instead)

Discover why data, object, value, and sealed are quietly replacing the traditional class—and how they can clean up your Kotlin code for good.

--

Press enter or click to view image in full size
Photo by Leighton Smith on Unsplash

Not a Medium Member? ā€œRead for Freeā€

When I first started writing Kotlin, I used class like I did in Java—because that’s what I knew.

class User(val name: String, val age: Int)

Simple, right? But over time, I started noticing something odd:

I wasn’t really using plain class anymore.

Instead, I was gravitating toward:

  • data class for models
  • object for singletons
  • sealed class for state management
  • value class for wrappers
  • interface with default methods for behaviour

And soon, class started to feel… unnecessary.

This wasn’t just a style preference — it was a productivity boost, a readability win, and a bug-preventing habit.

In this article, I’ll show you why I stopped using class in most of my Kotlin code, and what you should be using instead.

1. Use data class for Models — It’s Just Smarter

If your class is just holding data, like most models do, you’re wasting time using regular class.

// Don’t do this
class User(val name: String, val age: Int)

You’ll miss out on:

  • Auto-generated equals(), hashCode(), toString(), and copy()
  • Structural equality checks
  • Easy destructuring

Instead, use:

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

This alone reduces boilerplate and improves correctness.

2. Use object for Singletons — No More Manual Patterns

You don’t need to write Singleton boilerplate like this anymore:

class Logger private constructor() {
companion object {
val instance = Logger()
}
}

Instead, just use:

object Logger {
fun log(msg: String) = println(msg)
}

It’s cleaner, safer, and Kotlin-native. object handles thread-safety and initialization out of the box.

3. Use sealed class for UI State and Navigation

Need polymorphism without accidental inheritance? A regular class is risky.

Use sealed to tightly control your type hierarchy:

sealed class ApiState<out T> {
data class Success<out R>(val data: R) : ApiState<R>()
data class Failure(val msg: String) : ApiState<Nothing>()
data object Loading : ApiState<Nothing>()
}

This gives you:

  • Exhaustive when statements
  • Compile-time safety
  • Clearer domain modeling

4. Use value class (JVM inline class) for Type Safety Without Overhead

Say you have something like this:

class Email(val value: String)

It adds type safety, but at a runtime cost. Why create an object just to wrap a primitive?

Instead, use @JvmInline value class:

@JvmInline
value class Email(val value: String)

Now, you get type safety and zero runtime allocation. It’s great for IDs, wrappers, and primitives-as-types.

5. Use interface With Default Methods for Composition

Prefer composition over inheritance? Kotlin makes this clean:

interface Clickable {
fun onClick() = println("Clicked!")
}

Then mix it into your class:

class Button : Clickable

No need for abstract base classes just to reuse behaviour.

To be fair, class isn’t dead. There are still a few cases where it makes sense:

  • When building a mutable state holder (e.g., ViewModel)
  • When extending Java classes that require open inheritance
  • When you need non-final classes for testing (though this can often be avoided)

But these are the exceptions, not the rule.

Conclusion

Learning Kotlin isn’t just about syntax — it’s about adopting a mindset that values clarity, safety, and brevity.

Letting go of class in favour of Kotlin’s modern constructs helped me:

  • Write more expressive code
  • Reduce boilerplate
  • Avoid bugs
  • Think more functionally

So the next time you reach for class, stop and ask:

ā€œIs there a better Kotlin-native way to model this?ā€

Chances are, there is. And once you get used to it, you won’t look back.

--

--

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.

Responses (8)