In Kotlin, enum class is used for a fixed set of constants. Enums can have:
- simple constants
- constructor parameters / associated data
- properties
- functions
- overridden behavior per constant
- companion object utilities
- implemented interfaces
Basic enum
enum class Direction {
NORTH,
SOUTH,
EAST,
WEST
}
Usage:
val direction = Direction.NORTH
when (direction) {
Direction.NORTH -> println("Going up")
Direction.SOUTH -> println("Going down")
Direction.EAST -> println("Going right")
Direction.WEST -> println("Going left")
}
Kotlin when is exhaustive for enums if you cover all constants, so you often do not need an else.
Enum with associated data
Enums can have a constructor.
enum class HttpStatus(val code: Int, val reason: String) {
OK(200, "OK"),
CREATED(201, "Created"),
BAD_REQUEST(400, "Bad Request"),
NOT_FOUND(404, "Not Found"),
INTERNAL_SERVER_ERROR(500, "Internal Server Error")
}
Usage:
val status = HttpStatus.NOT_FOUND
println(status.code) // 404
println(status.reason) // Not Found
Important syntax rule: if an enum has members after the constants, the constant list must end with a semicolon.
enum class HttpStatus(val code: Int) {
OK(200),
NOT_FOUND(404);
fun isSuccess(): Boolean = code in 200..299
}
Enum with shared behavior
You can define functions inside the enum class.
enum class Planet(val mass: Double, val radius: Double) {
EARTH(5.972e24, 6.371e6),
MARS(6.39e23, 3.389e6),
JUPITER(1.898e27, 6.9911e7);
fun surfaceGravity(): Double {
val gravitationalConstant = 6.67430e-11
return gravitationalConstant * mass / (radius * radius)
}
}
Usage:
println(Planet.EARTH.surfaceGravity())
Enum constants with different behavior
Each enum constant can override functions.
enum class Operation {
PLUS {
override fun apply(a: Int, b: Int): Int = a + b
},
MINUS {
override fun apply(a: Int, b: Int): Int = a - b
},
TIMES {
override fun apply(a: Int, b: Int): Int = a * b
},
DIVIDE {
override fun apply(a: Int, b: Int): Int = a / b
};
abstract fun apply(a: Int, b: Int): Int
}
Usage:
val result = Operation.TIMES.apply(6, 7)
println(result) // 42
This pattern is useful when the enum represents a strategy or command.
Enum implementing an interface
Enums can implement interfaces.
interface Printable {
fun label(): String
}
enum class Priority : Printable {
LOW {
override fun label(): String = "Low priority"
},
MEDIUM {
override fun label(): String = "Medium priority"
},
HIGH {
override fun label(): String = "High priority"
}
}
Usage:
val priority: Printable = Priority.HIGH
println(priority.label())
You can also combine constructor data with an interface:
interface HasCode {
val code: Int
}
enum class ErrorType(
override val code: Int,
val message: String
) : HasCode {
VALIDATION(100, "Validation failed"),
AUTHENTICATION(200, "Authentication failed"),
NOT_FOUND(300, "Resource not found")
}
Companion object lookup helpers
A common pattern is looking up enum values by associated data.
enum class HttpStatus(val code: Int) {
OK(200),
CREATED(201),
BAD_REQUEST(400),
NOT_FOUND(404);
companion object {
fun fromCode(code: Int): HttpStatus? {
return entries.find { it.code == code }
}
}
}
Usage:
val status = HttpStatus.fromCode(404)
println(status) // NOT_FOUND
In modern Kotlin, prefer entries over values():
HttpStatus.entries
instead of:
HttpStatus.values()
Built-in enum properties and functions
Every enum constant has:
val name: String
val ordinal: Int
Example:
enum class Color {
RED,
GREEN,
BLUE
}
println(Color.RED.name) // RED
println(Color.RED.ordinal) // 0
You can parse by name:
val color = enumValueOf<Color>("RED")
println(color) // RED
Or safely:
val color = Color.entries.find { it.name == "RED" }
Enum with custom display names
Avoid relying on name for user-facing text. Use a property instead.
enum class UserRole(val displayName: String) {
ADMIN("Administrator"),
EDITOR("Editor"),
VIEWER("Viewer")
}
Usage:
println(UserRole.ADMIN.displayName) // Administrator
Enum with properties and computed values
enum class FileType(val extension: String) {
TEXT("txt"),
JSON("json"),
CSV("csv");
val mimeType: String
get() = when (this) {
TEXT -> "text/plain"
JSON -> "application/json"
CSV -> "text/csv"
}
}
Usage:
println(FileType.JSON.extension) // json
println(FileType.JSON.mimeType) // application/json
When to use enums
Use an enum when:
- the set of values is fixed
- each value is a singleton
- you need exhaustive
whenhandling - the values are known at compile time
Good examples:
enum class LogLevel {
TRACE,
DEBUG,
INFO,
WARN,
ERROR
}
enum class PaymentStatus {
PENDING,
PAID,
FAILED,
REFUNDED
}
When not to use enums
If each variant needs different state shapes, consider a sealed class or sealed interface.
For example, this is better as a sealed type:
sealed interface UiState {
data object Loading : UiState
data class Success(val data: String) : UiState
data class Error(val message: String) : UiState
}
Because Success and Error need per-instance data, not fixed singleton enum constants.
Quick summary
enum class Status(val code: Int) {
ACTIVE(1),
DISABLED(2),
DELETED(3);
fun isVisible(): Boolean = this != DELETED
companion object {
fun fromCode(code: Int): Status? =
entries.find { it.code == code }
}
}
Usage:
val status = Status.fromCode(1)
if (status?.isVisible() == true) {
println("Show item")
}
Kotlin enums are best for fixed named values, and they can carry data and behavior just like small classes.
