Understanding Different Types of Classes in Kotlin with Real-World Examples


Kotlin provides various types of classes, each designed for specific use cases. In this blog, we will break them down using real-world analogies, code examples, and key differences.
1️⃣ Normal Class (class
)
A normal class is used to create objects that can store data and have methods. When we compare two instances, they are compared by reference, meaning two different objects are never equal even if they hold the same values.
Real-World Example
Imagine a Car Factory 🚗🚗🚗 that produces cars. Each car has a different identity (even if they have the same model and color).
Implementation
class Car(val model: String, val color: String)
fun main() {
val car1 = Car("Tesla", "Red")
val car2 = Car("Tesla", "Red")
println(car1 == car2) // false (compared by reference)
}
✅ Two objects with the same values are still different instances.
2️⃣ Data Class (data class
)
A data class is optimized for storing data. Unlike normal classes, instances are compared by content, not reference. It automatically provides equals()
, hashCode()
, and toString()
functions.
Real-World Example
Think of User Profiles in an app. Two users with the same name and email should be considered equal, even if they are different objects.
Implementation
data class User(val name: String, val email: String)
fun main() {
val user1 = User("Aditya", "aditya@example.com")
val user2 = User("Aditya", "aditya@example.com")
println(user1 == user2) // true (compared by content)
}
✅ data class
instances are compared by their actual data, not memory reference.
3️⃣ Singleton (object
)
A singleton is a class that only allows one instance throughout the application.
Real-World Example
Think of a Logger System 📜 in an application. There should only be one logging system handling all logs.
Implementation
object Logger {
fun log(message: String) {
println("LOG: $message")
}
}
fun main() {
Logger.log("App started")
Logger.log("User logged in")
}
✅ You cannot create multiple instances of Logger
(Logger()
is not allowed).
❌ We cannot do this:
val logger1 = Logger() // ❌ ERROR! Cannot create a new instance
val logger2 = Logger() // ❌ ERROR!
What "Creating Multiple Instances" Means
Let’s compare with a real-world example:
Concept | Real-World Example |
Multiple instances (normal class) | A car factory making many cars (each car is different) 🚗🚗🚗 |
Single instance (object/data object) | A traffic signal system in a city 🚦 (only one system for all) |
4️⃣ Data Object (data object
)
A data object is a singleton but specifically for immutable data storage.
Real-World Example
Think of App Configurations ⚙️ that store settings like API keys.
Implementation
data object AppConfig {
val apiKey: String = "ABC123XYZ"
}
fun main() {
println(AppConfig.apiKey) // ABC123XYZ
}
✅ Useful for storing constant, read-only data in a singleton format.
Key Differences (Simple Table)
Feature | Singleton (object ) | Data Object (data object ) |
Purpose | Shared utilities, global logic | Single-instance immutable data |
Can store data? | ✅ Yes, but usually used for functions | ✅ Yes, main purpose is to store data |
Can modify values? | ✅ Yes, if variables are var | ❌ No, only val (immutable) |
Use case | Logger, DatabaseHelper | App settings, default configurations |
Example | Logger.log("App started") | AppConfig.theme |
Final Analogy
Concept | Example in Real Life |
Singleton (object ) | TV remote 🕹️ (one instance, shared by everyone) |
Data Object (data object ) | Wi-Fi settings 📶 (one copy, read-only) |
5️⃣ Enum Class (enum class
)
An enum class is used when you need a fixed set of values that won’t change.
Real-World Example
Think of Traffic Lights 🚦 - the colors Red, Yellow, Green are predefined and never change.
Implementation
enum class TrafficLight {
RED, YELLOW, GREEN
}
fun main() {
val currentLight = TrafficLight.RED
when (currentLight) {
TrafficLight.RED -> println("Stop 🚦")
TrafficLight.YELLOW -> println("Get Ready ⚠️")
TrafficLight.GREEN -> println("Go! 🏎️")
}
}
✅ Enum values are fixed at compile time and cannot be changed dynamically.
6️⃣ Sealed Class (sealed class
)
A sealed class is similar to an enum, but each type can hold different data.
Real-World Example
Think of Bank Transactions 💰:
Success → Stores a transaction ID.
Failure → Stores an error message.
Pending → No extra data.
Implementation
sealed class Transaction {
data class Success(val transactionId: String) : Transaction()
data class Failure(val errorMessage: String) : Transaction()
data object Pending : Transaction()
}
fun handleTransaction(transaction: Transaction) {
when (transaction) {
is Transaction.Success -> println("Transaction Successful! ID: ${transaction.transactionId}")
is Transaction.Failure -> println("Transaction Failed! Error: ${transaction.errorMessage}")
is Transaction.Pending -> println("Transaction Pending... Please wait.")
}
}
fun main() {
handleTransaction(Transaction.Success("TXN12345"))
handleTransaction(Transaction.Failure("Insufficient Balance"))
handleTransaction(Transaction.Pending)
}
✅ Allows different types with different data, unlike enums.
7️⃣ Key Differences (Comparison Table)
Feature | Enum Class (enum ) | Sealed Class (sealed class ) |
Use case | Fixed set of constants | Limited set of types that store different data |
Data storage | Each value is just a name | Each type can hold different properties |
Can add new values dynamically? | ❌ No | ✅ Yes |
Use when statement? | ✅ Yes, but must cover all values | ✅ Yes, but no need for else case |
Example | Traffic lights 🚦 | Bank Transactions 💰 |
Conclusion
Use Normal Class (
class
) for creating multiple instances.Use Data Class (
data class
) for objects that store data.Use Singleton (
object
) when only one instance is needed.Use Data Object (
data object
) for immutable global data.Use Enum (
enum class
) for a fixed set of constants.Use Sealed Class (
sealed class
) when you need different types with different data.
Each class type serves a specific purpose, and understanding their differences will help you write cleaner and more efficient Kotlin code. 🚀
Useful References
Subscribe to my newsletter
Read articles from Aditya Das directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Aditya Das
Aditya Das
B.Tech student in Information Technology with a passion for Android app development. Specializing in Kotlin, Java, and modern development practices. Exploring the latest trends in mobile and web technologies, and committed to sharing knowledge and insights through blogging. Follow along for tutorials, tips, and industry updates!