Kotlin Enum

The kotlin enum class is a special type of class that represents a fixed set of named constants. Each enum constant is essentially an object instance of the enum class, providing type safety and preventing invalid values from being assigned to variables. In Kotlin, enum classes are declared using the enum class keywords, distinguishing them from Java’s simple enum keyword.

enum class Direction {
    NORTH, SOUTH, EAST, WEST
}

This basic kotlin enum declaration creates four constants that can be used throughout your application with complete type safety.

Basic Kotlin Enum Class Syntax

The fundamental syntax for creating a kotlin enum class follows this pattern:

enum class EnumName {
    CONSTANT1,
    CONSTANT2,
    CONSTANT3
}

Simple Enum Example

enum class Priority {
    LOW, MEDIUM, HIGH, URGENT
}

fun handleTask(priority: Priority) {
    when (priority) {
        Priority.LOW -> println("Handle when convenient")
        Priority.MEDIUM -> println("Handle within a day")
        Priority.HIGH -> println("Handle within hours")
        Priority.URGENT -> println("Handle immediately")
    }
}

The kotlin enum constants follow the UPPER_SNAKE_CASE naming convention and are accessed using dot notation: Priority.HIGH.

Kotlin Enum Class with Properties

One of the most powerful features of kotlin enum class is the ability to add properties to enum constants. Each constant can have associated data, making enums much more expressive than simple constant values.

enum class HttpStatusCode(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")
}

In this kotlin enum example, each status code has both a numeric code and a descriptive message. You can access these properties directly:

val status = HttpStatusCode.NOT_FOUND
println("Status: ${status.code} - ${status.message}")
// Output: Status: 404 - Not Found

Multiple Property Types

Kotlin enum class can have properties of different types, including nullable types and complex objects:

enum class Planet(
    val mass: Double,           // in kg
    val radius: Double,         // in meters
    val atmosphere: String?     // nullable
) {
    MERCURY(3.303e23, 2.4397e6, null),
    VENUS(4.869e24, 6.0518e6, "CO2"),
    EARTH(5.976e24, 6.37814e6, "N2, O2"),
    MARS(6.421e23, 3.3972e6, "CO2")
}

Built-in Properties and Methods

Every kotlin enum automatically provides several built-in properties and methods:

The name Property

The name property returns the string representation of the enum constant:

enum class Color {
    RED, GREEN, BLUE
}

val color = Color.RED
println(color.name) // Output: RED

The ordinal Property

The ordinal property returns the zero-based position of the enum constant:

enum class Weekday {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
}

val day = Weekday.WEDNESDAY
println(day.ordinal) // Output: 3

The entries Property

The entries property (introduced in Kotlin 1.9.0) returns an immutable list of all enum constants:

enum class Season {
    SPRING, SUMMER, AUTUMN, WINTER
}

fun printAllSeasons() {
    for (season in Season.entries) {
        println("Season: ${season.name}")
    }
}

The valueOf() Method

The valueOf() method converts a string to the corresponding enum constant:

val colorFromString = Color.valueOf("BLUE")
println(colorFromString) // Output: BLUE

// Throws IllegalArgumentException if no match found
try {
    val invalid = Color.valueOf("PURPLE")
} catch (e: IllegalArgumentException) {
    println("Color not found: ${e.message}")
}

Kotlin Enum Class with Methods

Kotlin enum class can define both abstract and concrete methods, allowing each constant to have specialized behavior:

Abstract Methods in Enum

enum class Operation {
    ADD {
        override fun calculate(x: Int, y: Int) = x + y
    },
    SUBTRACT {
        override fun calculate(x: Int, y: Int) = x - y
    },
    MULTIPLY {
        override fun calculate(x: Int, y: Int) = x * y
    },
    DIVIDE {
        override fun calculate(x: Int, y: Int) = if (y != 0) x / y else 0
    };
    
    abstract fun calculate(x: Int, y: Int): Int
}

Note the semicolon (;) after the last enum constant when defining members below.

Concrete Methods in Enum

enum class FileType(val extension: String) {
    IMAGE("jpg"),
    DOCUMENT("pdf"),
    VIDEO("mp4"),
    AUDIO("mp3");
    
    fun getFullFileName(baseName: String): String {
        return "$baseName.$extension"
    }
    
    fun isMediaFile(): Boolean {
        return this == IMAGE || this == VIDEO || this == AUDIO
    }
}

// Usage
val imageFile = FileType.IMAGE
println(imageFile.getFullFileName("vacation")) // vacation.jpg
println(imageFile.isMediaFile()) // true

Companion Objects in Kotlin Enum

Kotlin enum class can include companion objects to provide utility functions that operate on the enum:

enum class UserRole(val level: Int, val permissions: List<String>) {
    GUEST(0, listOf("read")),
    USER(1, listOf("read", "write")),
    ADMIN(2, listOf("read", "write", "delete")),
    SUPER_ADMIN(3, listOf("read", "write", "delete", "manage"));
    
    companion object {
        fun fromLevel(level: Int): UserRole? {
            return entries.find { it.level == level }
        }
        
        fun getHighestRole(): UserRole {
            return entries.maxByOrNull { it.level } ?: GUEST
        }
        
        fun getAllPermissions(): Set<String> {
            return entries.flatMap { it.permissions }.toSet()
        }
    }
}

// Usage
val adminRole = UserRole.fromLevel(2)
val topRole = UserRole.getHighestRole()
val allPerms = UserRole.getAllPermissions()

Kotlin Enum with When Expression

One of the most elegant features of kotlin enum is its integration with the when expression. When all enum constants are handled, no else clause is required:

enum class NetworkState {
    IDLE, LOADING, SUCCESS, ERROR
}

fun handleNetworkState(state: NetworkState): String {
    return when (state) {
        NetworkState.IDLE -> "Ready to make request"
        NetworkState.LOADING -> "Request in progress..."
        NetworkState.SUCCESS -> "Data loaded successfully"
        NetworkState.ERROR -> "Failed to load data"
        // No else clause needed - all cases covered
    }
}

Interface Implementation

Kotlin enum class can implement interfaces, providing either common implementations or specific implementations per constant:

interface Drawable {
    fun draw(): String
}

enum class Shape : Drawable {
    CIRCLE {
        override fun draw() = "Drawing a circle with curves"
    },
    SQUARE {
        override fun draw() = "Drawing a square with four sides"
    },
    TRIANGLE {
        override fun draw() = "Drawing a triangle with three sides"
    }
}

// Usage
val shapes = Shape.entries
shapes.forEach { shape ->
    println(shape.draw())
}

Advanced Kotlin Enum Patterns

Generic Extension Functions

You can create generic extension functions that work with any kotlin enum:

inline fun <reified T : Enum<T>> enumValues(): Array<T> {
    return enumValues<T>()
}

inline fun <reified T : Enum<T>> T.next(): T {
    val values = enumValues<T>()
    val nextOrdinal = (ordinal + 1) % values.size
    return values[nextOrdinal]
}

inline fun <reified T : Enum<T>> T.previous(): T {
    val values = enumValues<T>()
    val prevOrdinal = if (ordinal == 0) values.size - 1 else ordinal - 1
    return values[prevOrdinal]
}

// Usage with any enum
val currentDay = Weekday.TUESDAY
val nextDay = currentDay.next()      // WEDNESDAY
val prevDay = currentDay.previous()  // MONDAY

Enum with Complex State Management

For Android development, kotlin enum class is perfect for managing application states:

enum class AppState(
    val isLoading: Boolean,
    val canInteract: Boolean,
    val showProgress: Boolean
) {
    INITIALIZING(true, false, true) {
        override fun getStateMessage() = "App is starting up..."
        override fun nextState() = READY
    },
    READY(false, true, false) {
        override fun getStateMessage() = "Ready for user interaction"
        override fun nextState() = PROCESSING
    },
    PROCESSING(true, false, true) {
        override fun getStateMessage() = "Processing request..."
        override fun nextState() = READY
    },
    ERROR(false, true, false) {
        override fun getStateMessage() = "An error occurred"
        override fun nextState() = READY
    };
    
    abstract fun getStateMessage(): String
    abstract fun nextState(): AppState
    
    companion object {
        fun getInitialState() = INITIALIZING
    }
}

Real-World Android Example

Here’s a comprehensive example showing how to use kotlin enum class in Android development for managing different screen orientations and their properties:

enum class ScreenOrientation(
    val degrees: Int,
    val isPortrait: Boolean,
    val layoutResource: Int
) {
    PORTRAIT(0, true, R.layout.activity_main_portrait) {
        override fun getDisplayMetrics() = "Portrait: Tall and narrow"
        override fun getOptimalImageSize() = Pair(400, 600)
    },
    LANDSCAPE(90, false, R.layout.activity_main_landscape) {
        override fun getDisplayMetrics() = "Landscape: Wide and short"
        override fun getOptimalImageSize() = Pair(600, 400)
    },
    REVERSE_PORTRAIT(180, true, R.layout.activity_main_portrait) {
        override fun getDisplayMetrics() = "Reverse Portrait: Upside down"
        override fun getOptimalImageSize() = Pair(400, 600)
    },
    REVERSE_LANDSCAPE(270, false, R.layout.activity_main_landscape) {
        override fun getDisplayMetrics() = "Reverse Landscape: Rotated wide"
        override fun getOptimalImageSize() = Pair(600, 400)
    };
    
    abstract fun getDisplayMetrics(): String
    abstract fun getOptimalImageSize(): Pair<Int, Int>
    
    fun getRotationAngle(): Float = degrees.toFloat()
    
    fun isLandscape(): Boolean = !isPortrait
    
    companion object {
        fun fromDegrees(degrees: Int): ScreenOrientation? {
            return entries.find { it.degrees == degrees }
        }
        
        fun getPortraitOrientations(): List<ScreenOrientation> {
            return entries.filter { it.isPortrait }
        }
        
        fun getLandscapeOrientations(): List<ScreenOrientation> {
            return entries.filter { !it.isPortrait }
        }
    }
}

// Android Activity usage
class MainActivity : AppCompatActivity() {
    private var currentOrientation = ScreenOrientation.PORTRAIT
    
    private fun handleOrientationChange(newOrientation: ScreenOrientation) {
        currentOrientation = newOrientation
        
        // Set appropriate layout
        setContentView(newOrientation.layoutResource)
        
        // Configure UI based on orientation
        when (newOrientation) {
            ScreenOrientation.PORTRAIT, ScreenOrientation.REVERSE_PORTRAIT -> {
                configurePortraitLayout()
            }
            ScreenOrientation.LANDSCAPE, ScreenOrientation.REVERSE_LANDSCAPE -> {
                configureLandscapeLayout()
            }
        }
        
        // Log orientation info
        Log.d("Orientation", newOrientation.getDisplayMetrics())
        
        // Adjust image sizes
        val (width, height) = newOrientation.getOptimalImageSize()
        adjustImageDimensions(width, height)
    }
    
    private fun configurePortraitLayout() {
        // Portrait-specific configuration
        supportActionBar?.show()
    }
    
    private fun configureLandscapeLayout() {
        // Landscape-specific configuration
        supportActionBar?.hide()
    }
    
    private fun adjustImageDimensions(width: Int, height: Int) {
        // Adjust image view dimensions based on orientation
        findViewById<ImageView>(R.id.main_image)?.apply {
            layoutParams.width = width
            layoutParams.height = height
            requestLayout()
        }
    }
}

Complete Example with All Features

Here’s a comprehensive example that demonstrates all major kotlin enum class features:

import android.util.Log

// Interface for drawable items
interface Renderable {
    fun render(): String
}

// Main enum class with all features
enum class GameCharacter(
    val healthPoints: Int,
    val attackPower: Int,
    val defense: Int,
    val specialAbility: String?
) : Renderable {
    
    WARRIOR(100, 25, 20, "Shield Bash") {
        override fun render() = "⚔️ Mighty warrior with sword and shield"
        override fun performSpecialAttack(target: GameCharacter): Int {
            return (attackPower * 1.5).toInt()
        }
    },
    
    MAGE(70, 35, 10, "Fireball") {
        override fun render() = "🧙‍♂️ Wise mage with magical staff"
        override fun performSpecialAttack(target: GameCharacter): Int {
            return if (target == WARRIOR) attackPower * 2 else (attackPower * 1.3).toInt()
        }
    },
    
    ARCHER(80, 30, 15, "Multi-Shot") {
        override fun render() = "🏹 Skilled archer with enchanted bow"
        override fun performSpecialAttack(target: GameCharacter): Int {
            return (attackPower * 1.4).toInt()
        }
    },
    
    ROGUE(60, 40, 8, "Stealth Strike") {
        override fun render() = "🗡️ Stealthy rogue with twin daggers"
        override fun performSpecialAttack(target: GameCharacter): Int {
            return attackPower * 2 // Critical hit
        }
    };
    
    // Abstract method that each constant must implement
    abstract fun performSpecialAttack(target: GameCharacter): Int
    
    // Concrete methods available to all constants
    fun calculateTotalStats(): Int {
        return healthPoints + attackPower + defense
    }
    
    fun getCharacterType(): String {
        return when (this) {
            WARRIOR -> "Tank"
            MAGE -> "Damage Dealer"
            ARCHER -> "Ranged DPS"
            ROGUE -> "Assassin"
        }
    }
    
    fun isStrongAgainst(other: GameCharacter): Boolean {
        return when (this) {
            WARRIOR -> other == ROGUE
            MAGE -> other == WARRIOR
            ARCHER -> other == MAGE
            ROGUE -> other == ARCHER
        }
    }
    
    // Companion object with utility functions
    companion object {
        fun getStrongestCharacter(): GameCharacter {
            return entries.maxByOrNull { it.calculateTotalStats() } ?: WARRIOR
        }
        
        fun getWeakestCharacter(): GameCharacter {
            return entries.minByOrNull { it.calculateTotalStats() } ?: ROGUE
        }
        
        fun findBySpecialAbility(ability: String): GameCharacter? {
            return entries.find { it.specialAbility == ability }
        }
        
        fun getCharactersByType(type: String): List<GameCharacter> {
            return entries.filter { it.getCharacterType() == type }
        }
        
        fun createRandomTeam(size: Int): List<GameCharacter> {
            return entries.shuffled().take(size)
        }
        
        @JvmStatic
        fun getAllSpecialAbilities(): List<String> {
            return entries.mapNotNull { it.specialAbility }
        }
    }
}

// Usage example in Android application
class GameActivity {
    
    fun demonstrateEnumFeatures() {
        // Basic enum usage
        val player = GameCharacter.MAGE
        Log.d("Game", "Selected character: ${player.name}")
        Log.d("Game", "Character render: ${player.render()}")
        
        // Accessing properties
        Log.d("Game", "Health: ${player.healthPoints}")
        Log.d("Game", "Attack: ${player.attackPower}")
        Log.d("Game", "Defense: ${player.defense}")
        Log.d("Game", "Special: ${player.specialAbility}")
        
        // Using methods
        Log.d("Game", "Total stats: ${player.calculateTotalStats()}")
        Log.d("Game", "Character type: ${player.getCharacterType()}")
        
        // Combat example
        val enemy = GameCharacter.WARRIOR
        val damage = player.performSpecialAttack(enemy)
        Log.d("Game", "${player.name} attacks ${enemy.name} for $damage damage!")
        
        // Using when expression
        val battleResult = when (player) {
            GameCharacter.WARRIOR -> "Charges into battle!"
            GameCharacter.MAGE -> "Casts powerful spells!"
            GameCharacter.ARCHER -> "Shoots arrows with precision!"
            GameCharacter.ROGUE -> "Strikes from the shadows!"
        }
        Log.d("Game", "Battle action: $battleResult")
        
        // Using companion object functions
        val strongest = GameCharacter.getStrongestCharacter()
        Log.d("Game", "Strongest character: ${strongest.name}")
        
        val fireballUser = GameCharacter.findBySpecialAbility("Fireball")
        Log.d("Game", "Fireball user: ${fireballUser?.name}")
        
        val randomTeam = GameCharacter.createRandomTeam(3)
        Log.d("Game", "Random team: ${randomTeam.map { it.name }}")
        
        // Iterating through all characters
        GameCharacter.entries.forEach { character ->
            Log.d("Game", "${character.name}: ${character.render()}")
        }
        
        // Type advantages
        val advantages = GameCharacter.entries.map { char1 ->
            val strongAgainst = GameCharacter.entries.filter { char1.isStrongAgainst(it) }
            "${char1.name} is strong against: ${strongAgainst.map { it.name }}"
        }
        advantages.forEach { Log.d("Game", it) }
    }
}

// Extension functions for additional functionality
fun GameCharacter.getHealthStatus(): String {
    return when {
        healthPoints >= 80 -> "Excellent health"
        healthPoints >= 60 -> "Good health"
        healthPoints >= 40 -> "Fair health"
        else -> "Poor health"
    }
}

fun GameCharacter.canDefeat(other: GameCharacter): Boolean {
    val myEffectivePower = if (isStrongAgainst(other)) attackPower * 1.5 else attackPower.toDouble()
    val theirEffectivePower = if (other.isStrongAgainst(this)) other.attackPower * 1.5 else other.attackPower.toDouble()
    
    return myEffectivePower > theirEffectivePower
}

// Generic enum utilities
inline fun <reified T : Enum<T>> T.getNextValue(): T {
    val values = enumValues<T>()
    val nextIndex = (ordinal + 1) % values.size
    return values[nextIndex]
}

inline fun <reified T : Enum<T>> randomEnumValue(): T {
    val values = enumValues<T>()
    return values.random()
}

fun main() {
    // Demonstration of complete enum functionality
    val gameActivity = GameActivity()
    gameActivity.demonstrateEnumFeatures()
    
    // Additional examples
    val randomCharacter = randomEnumValue<GameCharacter>()
    println("Random character: ${randomCharacter.name}")
    
    val nextCharacter = randomCharacter.getNextValue()
    println("Next character: ${nextCharacter.name}")
    
    val healthStatus = randomCharacter.getHealthStatus()
    println("Health status: $healthStatus")
}

This comprehensive example demonstrates every aspect of kotlin enum class functionality, from basic constant declaration to advanced features like abstract methods, interface implementation, companion objects, and extension functions. The GameCharacter enum showcases how powerful and flexible Kotlin enums can be in real-world Android development scenarios.