Kotlin Data Classes and Enums
Kotlin provides two specialized class types — data classes and enums — that solve common programming problems with very little code. Data classes make it easy to hold and compare data. Enums represent a fixed set of named values.
Data Classes
A data class is a class whose main purpose is to hold data. Kotlin automatically generates several useful methods for you: equals(), hashCode(), toString(), and copy().
Defining a Data Class
data class Student(
val name: String,
val rollNo: Int,
val grade: String
)
What Kotlin Generates Automatically
val s1 = Student("Priya", 101, "A")
val s2 = Student("Priya", 101, "A")
val s3 = Student("Arun", 102, "B")
// toString() — readable output
println(s1) // Student(name=Priya, rollNo=101, grade=A)
// equals() — compares content, not reference
println(s1 == s2) // true (same data)
println(s1 == s3) // false (different data)
// copy() — create a modified copy
val updated = s1.copy(grade = "A+")
println(updated) // Student(name=Priya, rollNo=101, grade=A+)
println(s1) // Student(name=Priya, rollNo=101, grade=A) — unchanged
Diagram — Regular Class vs Data Class
Regular class — what you write AND what you must add manually:
class Point(val x: Int, val y: Int) {
override fun equals(other: Any?) = ... // write yourself
override fun hashCode() = ... // write yourself
override fun toString() = ... // write yourself
fun copy(...) = ... // write yourself
}
Data class — Kotlin writes all of that for you:
data class Point(val x: Int, val y: Int)
Destructuring Declarations
Data classes support destructuring — you can unpack multiple properties into separate variables in one line.
val student = Student("Neha", 103, "B+")
val (name, roll, grade) = student
println(name) // Neha
println(roll) // 103
println(grade) // B+
// Useful in loops over a list of data objects
val students = listOf(
Student("Kiran", 104, "A"),
Student("Dev", 105, "B")
)
for ((n, r, g) in students) {
println("$r: $n — Grade $g")
}
Enum Classes
An enum (short for enumeration) defines a type with a fixed set of possible values. Use enums when a variable can only be one of a known set of choices — like days of the week, app states, or directions.
Basic Enum
enum class Direction {
NORTH, SOUTH, EAST, WEST
}
val heading = Direction.NORTH
println(heading) // NORTH
println(heading.name) // NORTH (as String)
println(heading.ordinal) // 0 (position in list, starts at 0)
Enum in when Expression
val move = Direction.EAST
when (move) {
Direction.NORTH -> println("Moving up")
Direction.SOUTH -> println("Moving down")
Direction.EAST -> println("Moving right")
Direction.WEST -> println("Moving left")
}
// Moving right
Enum with Properties and Methods
Enum constants can carry their own data and behavior.
enum class Planet(val massKg: Double, val radiusKm: Double) {
MERCURY(3.30e23, 2440.0),
VENUS (4.87e24, 6052.0),
EARTH (5.97e24, 6371.0),
MARS (6.42e23, 3390.0);
fun surfaceGravity(): Double {
val G = 6.67e-11
return G * massKg / (radiusKm * 1000) / (radiusKm * 1000)
}
}
println(Planet.EARTH.surfaceGravity()) // ~9.8 m/s²
println(Planet.MARS.name) // MARS
Diagram — Enum as a Sealed Set of Values
enum class Status { PENDING, ACTIVE, SUSPENDED, CLOSED }
│ │ │ │
allowed allowed allowed allowed
values values values values
val s: Status = Status.ACTIVE
s cannot be anything other than these four values.
This prevents typos and invalid states at compile time.
Iterating Over Enum Values
for (direction in Direction.values()) {
println(direction)
}
// NORTH
// SOUTH
// EAST
// WEST
// Get enum from string
val d = Direction.valueOf("SOUTH")
println(d) // SOUTH
Sealed Classes — Enums with Full Class Power
A sealed class is like an enum that allows each variant to carry different data. All subclasses must be in the same file. Use sealed classes to model result states like Success or Error.
sealed class ApiResult {
data class Success(val data: String) : ApiResult()
data class Error(val message: String, val code: Int) : ApiResult()
object Loading : ApiResult()
}
fun handleResult(result: ApiResult) {
when (result) {
is ApiResult.Success -> println("Data: ${result.data}")
is ApiResult.Error -> println("Error ${result.code}: ${result.message}")
is ApiResult.Loading -> println("Please wait...")
}
}
handleResult(ApiResult.Success("User loaded"))
// Data: User loaded
handleResult(ApiResult.Error("Not found", 404))
// Error 404: Not found
