Kotlin Enum Class

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.