Kotlin Enums
Type-safe constants and enumerated values in Kotlin
🏷️ What are Kotlin Enums?
Enums in Kotlin represent a fixed set of constants in a type-safe way. They can have properties, methods, and implement interfaces, making them more powerful than simple constants while ensuring compile-time safety and preventing invalid values.
// Simple enum example
enum class Direction {
NORTH, SOUTH, EAST, WEST
}
Enum Features
Type Safety
Compile-time constant validation
enum class Status { ACTIVE, INACTIVE }
// Only valid Status values allowed
Properties
Each constant can have properties
enum class Color(val hex: String) {
RED("#FF0000")
}
Methods
Enums can have methods
enum class Operation {
ADD;
fun calculate(a: Int, b: Int) = a + b
}
Built-in Methods
name, ordinal, values(), valueOf()
Direction.NORTH.name // "NORTH"
Direction.NORTH.ordinal // 0
🔹 Basic Enum Declaration
Here's how to create and use simple enums:
enum class Priority {
LOW, MEDIUM, HIGH, URGENT
}
enum class TaskStatus {
TODO, IN_PROGRESS, DONE, CANCELLED
}
fun main() {
val taskPriority = Priority.HIGH
val status = TaskStatus.IN_PROGRESS
println("Task priority: $taskPriority")
println("Task status: $status")
// Using built-in properties
println("Priority name: ${taskPriority.name}")
println("Priority ordinal: ${taskPriority.ordinal}")
// Get all enum values
println("All priorities:")
Priority.values().forEach { priority ->
println(" ${priority.ordinal}: ${priority.name}")
}
// Convert string to enum
val priorityFromString = Priority.valueOf("MEDIUM")
println("Priority from string: $priorityFromString")
}
Output:
Task priority: HIGH
Task status: IN_PROGRESS
Priority name: HIGH
Priority ordinal: 2
All priorities:
0: LOW
1: MEDIUM
2: HIGH
3: URGENT
Priority from string: MEDIUM
🔹 Enums with Properties
Enums can have properties and custom constructors:
enum class Planet(val mass: Double, val radius: Double) {
MERCURY(3.303e+23, 2.4397e6),
VENUS(4.869e+24, 6.0518e6),
EARTH(5.976e+24, 6.37814e6),
MARS(6.421e+23, 3.3972e6);
// Calculated property
val surfaceGravity: Double
get() = G * mass / (radius * radius)
fun surfaceWeight(mass: Double): Double {
return mass * surfaceGravity
}
companion object {
private const val G = 6.67300E-11
}
}
fun main() {
val earthWeight = 75.0 // kg
println("Weight on different planets:")
Planet.values().forEach { planet ->
val weight = planet.surfaceWeight(earthWeight)
println("${planet.name}: ${String.format("%.2f", weight)} kg")
}
println("\nPlanet details:")
println("Earth mass: ${Planet.EARTH.mass}")
println("Earth radius: ${Planet.EARTH.radius}")
println("Earth surface gravity: ${String.format("%.2f", Planet.EARTH.surfaceGravity)}")
}
Output:
Weight on different planets:
MERCURY: 28.24 kg
VENUS: 67.70 kg
EARTH: 75.00 kg
MARS: 28.39 kg
Planet details:
Earth mass: 5.976E24
Earth radius: 6378140.0
Earth surface gravity: 9.80
🔹 Enums with Methods
Enums can implement interfaces and have methods:
interface Executable {
fun execute(a: Int, b: Int): Int
}
enum class MathOperation : Executable {
ADD {
override fun execute(a: Int, b: Int) = a + b
},
SUBTRACT {
override fun execute(a: Int, b: Int) = a - b
},
MULTIPLY {
override fun execute(a: Int, b: Int) = a * b
},
DIVIDE {
override fun execute(a: Int, b: Int) = if (b != 0) a / b else 0
};
fun getSymbol(): String {
return when (this) {
ADD -> "+"
SUBTRACT -> "-"
MULTIPLY -> "*"
DIVIDE -> "/"
}
}
}
class Calculator {
fun calculate(operation: MathOperation, a: Int, b: Int): String {
val result = operation.execute(a, b)
val symbol = operation.getSymbol()
return "$a $symbol $b = $result"
}
}
fun main() {
val calculator = Calculator()
val a = 15
val b = 3
println("Calculator operations:")
MathOperation.values().forEach { operation ->
println(calculator.calculate(operation, a, b))
}
// Using specific operations
println("\nSpecific calculations:")
println("Addition: ${MathOperation.ADD.execute(10, 5)}")
println("Division: ${MathOperation.DIVIDE.execute(20, 4)}")
}
Output:
Calculator operations:
15 + 3 = 18
15 - 3 = 12
15 * 3 = 45
15 / 3 = 5
Specific calculations:
Addition: 15
Division: 5
🔹 Enums in When Expressions
Enums work perfectly with when expressions for exhaustive checking:
enum class HttpStatus(val code: Int, val message: String) {
OK(200, "OK"),
NOT_FOUND(404, "Not Found"),
INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
BAD_REQUEST(400, "Bad Request"),
UNAUTHORIZED(401, "Unauthorized")
}
fun handleHttpResponse(status: HttpStatus): String {
return when (status) {
HttpStatus.OK -> "✅ Request successful"
HttpStatus.NOT_FOUND -> "❌ Resource not found"
HttpStatus.BAD_REQUEST -> "⚠️ Invalid request"
HttpStatus.UNAUTHORIZED -> "🔒 Authentication required"
HttpStatus.INTERNAL_SERVER_ERROR -> "💥 Server error occurred"
// No else clause needed - exhaustive!
}
}
fun getStatusCategory(status: HttpStatus): String {
return when (status.code) {
in 200..299 -> "Success"
in 400..499 -> "Client Error"
in 500..599 -> "Server Error"
else -> "Unknown"
}
}
fun main() {
val responses = listOf(
HttpStatus.OK,
HttpStatus.NOT_FOUND,
HttpStatus.INTERNAL_SERVER_ERROR,
HttpStatus.UNAUTHORIZED
)
println("HTTP Response Handling:")
responses.forEach { status ->
println("${status.code} ${status.message}")
println(" ${handleHttpResponse(status)}")
println(" Category: ${getStatusCategory(status)}")
println()
}
}
Output:
HTTP Response Handling:
200 OK
✅ Request successful
Category: Success
404 Not Found
❌ Resource not found
Category: Client Error
500 Internal Server Error
💥 Server error occurred
Category: Server Error
🔹 Advanced Enum Features
Enums with companion objects and extension functions:
enum class FileType(val extension: String, val mimeType: String) {
PDF("pdf", "application/pdf"),
JPEG("jpg", "image/jpeg"),
PNG("png", "image/png"),
TXT("txt", "text/plain"),
JSON("json", "application/json");
companion object {
fun fromExtension(ext: String): FileType? {
return values().find { it.extension.equals(ext, ignoreCase = true) }
}
fun getImageTypes(): List<FileType> {
return values().filter { it.mimeType.startsWith("image/") }
}
}
fun isImage(): Boolean = mimeType.startsWith("image/")
fun getDescription(): String {
return when (this) {
PDF -> "Portable Document Format"
JPEG -> "JPEG Image"
PNG -> "PNG Image"
TXT -> "Plain Text"
JSON -> "JSON Data"
}
}
}
// Extension function
fun FileType.getMaxSize(): Long {
return when (this) {
FileType.PDF -> 10_000_000L // 10MB
FileType.JPEG, FileType.PNG -> 5_000_000L // 5MB
FileType.TXT -> 1_000_000L // 1MB
FileType.JSON -> 2_000_000L // 2MB
}
}
fun main() {
// Using companion object methods
val pdfType = FileType.fromExtension("PDF")
println("PDF type: $pdfType")
val imageTypes = FileType.getImageTypes()
println("Image types: ${imageTypes.map { it.name }}")
// Using instance methods
println("\nFile type details:")
FileType.values().forEach { type ->
println("${type.name}:")
println(" Extension: .${type.extension}")
println(" MIME: ${type.mimeType}")
println(" Description: ${type.getDescription()}")
println(" Is Image: ${type.isImage()}")
println(" Max Size: ${type.getMaxSize() / 1_000_000}MB")
println()
}
}
Output:
PDF type: PDF
Image types: [JPEG, PNG]
File type details:
PDF:
Extension: .pdf
MIME: application/pdf
Description: Portable Document Format
Is Image: false
Max Size: 10MB