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.
The fundamental syntax for creating a kotlin enum class follows this pattern:
enum class EnumName {
CONSTANT1,
CONSTANT2,
CONSTANT3
}
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
.
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
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")
}
Every kotlin enum automatically provides several built-in properties and methods:
name
PropertyThe 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
ordinal
PropertyThe 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
entries
PropertyThe 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}")
}
}
valueOf()
MethodThe 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 can define both abstract and concrete methods, allowing each constant to have specialized behavior:
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.
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
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()
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
}
}
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())
}
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
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
}
}
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()
}
}
}
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.