A useful feature while programming is having the ability to indicate that a variable only has a finite set of possible values. To accomplish this, most programming languages introduced the concept of enumerations.
Although enumerations usually represent just a mere named list of predefined constant values, Kotlin enums are much more than that. In fact, they are real classes, and not simple types or limited data structured.
This translates to the fact that they can have custom properties and methods, implement interfaces, use anonymous classes, and much more. Thus, Kotlin enum classes play a crucial role in the language.
Plus, employing enums makes your code more readable and less error-prone. This is why every Kotlin developer should know how to use them. So, let’s dive into enum classes and see everything you need to learn to master them.
In Java, enums are types. Specifically, the official documentation defines an enum type as “a special data type that enables a variable to be a set of predefined constants.” This means that the aforementioned variable must be equal to one of the predefined values. These values are constants, and represent the properties of the enum type.
Despite being a type, the Java enum
declaration actually creates a class behind the scenes. Thus, Java enums can include custom methods and properties. This, in addition to the default ones automatically added by the compiler. That’s it — nothing more can be done with Java enum types.
Unlike what happens in Java, Kotlin enums are classes natively, and not only behind the scenes. This is why they are called enum classes, as opposed to Java enum types. That prevents developers from considering them as just mere collections of constants, as may happen in Java.
As we are about to see, Kotlin enums are much more than that. Not only can they use anonymous classes, but also implement interfaces, just like any other Kotlin class. So, let’s forget Java enum types and start delving into Kotlin enum classes.
Let’s start exploring the most common features offered by Kotlin enums.
The most basic use case for Kotlin enum classes is to treat them as collections of constants. In this case, they are called type-safe enums and can be defined as follows:
enum class Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }
As you can see, the enum
keyword is followed by the class
keyword. This should prevent you from being fooled into thinking that Kotlin enums are mere types.
Then comes the enum class name. Finally, inside the body of the enum class, the possible comma-separated options called enum constants. Note that since they are constants, their names should always be in uppercase letters. This is what the simplest Kotlin enum class consists of.
Kotlin enums are classes, which means that they can have one or more constructors. Thus, you can initialize enum constants by passing the values required to one of the valid constructors. This is possible because enum constants are nothing other than instances of the enum class itself.
Let’s see how this works through an example:
enum class Day(val dayOfWeek: Int) { MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5), SATURDAY(6), SUNDAY(7) }
This way, each enum constant is associated with the relative number of the day of the week.
Usually, the constructor-based approach is used to provide enum constants with useful information or meaningful values. On of the most common cases is to provide them with a custom printableName
property. This is very useful when printing them, and can be achieved as follows:
enum class Day(val printableName: String) { MONDAY("Monday"), TUESDAY("Tuesday"), WEDNESDAY("Wednesday"), THURSDAY("Thursday"), FRIDAY("Friday"), SATURDAY("Saturday"), SUNDAY("Sunday") }
Kotlin enum classes come with a few inbuilt properties. Just like what happens in Java, they are automatically added to each enum class by the compiler. So, you can access them in any enum class instance. Let’s see them all:
ordinal
ordinal
allows you to retrieve where the current enum constant appears in the list. It is a zero-based index, which means that the first constant in the options list has value 0
, the second 1
, and so on. When implementing the Comparable
interface, this property will be used in sorting logic.name
name
returns the name of the enum constant as a string.Let’s see these two in action through the following example:
enum class Day(val dayOfWeek: Int) { MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5), SATURDAY(6), SUNDAY(7) } fun main() { for (day in DAY.values()) println("[${day.ordinal}] -> ${day.name} (${day.dayOfWeek}^ day of the week)") }
By running this code, you would get the following result:
[0] -> MONDAY (1^ day of the week) [1] -> TUESDAY (2^ day of the week) [2] -> WEDNESDAY (3^ day of the week) [3] -> THURSDAY (4^ day of the week) [4] -> FRIDAY (5^ day of the week) [5] -> SATURDAY (6^ day of the week) [6] -> SUNDAY (7^ day of the week)
As you can see, the string returned by the name
inbuilt property coincides with the constant itself. This does not make them very printable, and this is why adding a custom printableName
property might be useful.
Also, this example highlights why adding a custom index might be required as well. This is because ordinal
is a zero-based index meant to be used while programming rather than to provide informational content.
Now, it is time to delve into the most advanced and complicated features offered by Kotlin enum classes.
Custom properties and methods can be added to enum classes, just like in any other Kotlin class. What changes is the syntax, which must follow specific rules.
In particular, methods and properties must be added below the enum constants definition, after a semicolon. Just like any other property in Kotlin, you can provide them with a default value. Plus, enum classes can have both instance and static methods:
enum class Day { MONDAY(1, "Monday"), TUESDAY(2, "Tuesday"), WEDNESDAY(3, "Wednesday"), THURSDAY(4, "Thursday"), FRIDAY(5, "Friday"), SATURDAY(6, "Saturday"), SUNDAY(7, "Sunday"); // end of the constants // custom properties with default values var dayOfWeek: Int? = null var printableName : String? = null constructor() // custom constructors constructor( dayOfWeek: Int, printableName: String ) { this.dayOfWeek = dayOfWeek this.printableName = printableName } // custom method fun customToString(): String { return "[${dayOfWeek}] -> $printableName" } }
In this example, a custom constructor, two custom properties, and a custom instance method were added to the enum class. Properties and methods can be accessed through instances, which are the enum constants, with the following syntax:
// accessing the dayOfWeek property Day.MONDAY.dayOfWeek // accessing the customToString() method Day.MONDAY.customToString()
Keep in mind that Kotlin does not have the concept of static methods. However, the same result can be achieved by harnessing companion objects, which are supported by Kotlin enum classes. Methods defined inside companion objects do not depend on specific instances and be accessed statically. You can add one as follows:
enum class Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY; companion object { fun getNumberOfDays() = values().size } }
Now, you can access the getNumberOfDays()
method this way:
Day.getNumberOfDays()
As you can see, the method is called statically on the class and does not depend on any instance. Note that the synthetic static method values()
was used while implementing it. You are going to see what it is and how to use it very soon.
We can create anonymous classes to define specific enum constants. In contrast to Java, Kotlin enum classes support anonymous classes.
In particular, enum constants can be instantiated by anonymous classes. They just have to give an implementation to the abstract methods of the enum class itself. This can be achieved with the following syntax:
enum class Day { MONDAY { override fun nextDay() = TUESDAY }, TUESDAY { override fun nextDay() = WEDNESDAY }, WEDNESDAY { override fun nextDay() = THURSDAY }, THURSDAY { override fun nextDay() = FRIDAY }, FRIDAY { override fun nextDay() = SATURDAY }, SATURDAY { override fun nextDay() = SUNDAY }, SUNDAY { override fun nextDay() = MONDAY }; abstract fun nextDay(): Day }
As shown here, each enum constant is instantiated by declaring its own anonymous classes while overriding the required abstract method. This is just as it would happen in any other Kotlin anonymous class.
Although Kotlin enum classes cannot derive from a class, enum class, or an abstract class, they can actually implement one or more interfaces.
In this case, each enum constant must provide an implementation of interface methods. This can be achieved with a common implementation, as follows:
interface IDay { fun firstDay(): Day } enum class Day: IDay { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY; override fun firstDay(): Day { return MONDAY } }
Or by using anonymous classes as showed before:
interface IDay { fun nextDay(): Day } enum class Day: IDay { MONDAY { override fun nextDay() = TUESDAY }, TUESDAY { override fun nextDay() = WEDNESDAY }, WEDNESDAY { override fun nextDay() = THURSDAY }, THURSDAY { override fun nextDay() = FRIDAY }, FRIDAY { override fun nextDay() = SATURDAY }, SATURDAY { override fun nextDay() = SUNDAY }, SUNDAY { override fun nextDay() = MONDAY }; }
In both cases, each enum constant has the IDay
interface method implemented.
Now that you have seen both basic and advanced features, you have everything required to start using Kotlin enum classes. Let’s see them in action through the three most common use cases.
when
Enum classes are particularly useful when used with Kotlin’s when
conditional statement. This is because when
expressions must take each possible condition into account. In other words, they must be exhaustive.
Since enums offer a limited set of values by definition, Kotlin can use this to figure out if every condition was considered. If not, an error at compile time will be thrown. Let’s see enums in action with the when
expression:
enum class Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY } fun main (currentDay: Day) { when (currentDay) { Day.MONDAY -> work() Day.TUESDAY -> work() Day.WEDNESDAY -> work() Day.THURSDAY -> work() Day.FRIDAY -> work() Day.SATURDAY -> rest() Day.SUNDAY -> rest() } } fun work() { println("Working") } fun rest() { println("Resting") }
As just shown, enums allow you to differentiate logic based on their value. They also make your code more readable and less error-prone. This is because they establish the maximum number of possible options to be considered in a when
statement. This way, you cannot forget one.
Similar to the aforementioned inbuilt properties, every enum class also has synthetic methods. They are automatically added by Kotlin at compile time and represent utility functions that can be accessed statically. Let’s see the most important ones and how to use them:
values()
valueOf(value: String)
name
property matches the value string passed as a parameter. If not found, an IllegalArgumentException
is thrown.Let’s see them in action through an example:
enum class Day(val printableName: String) { MONDAY("Monday"), TUESDAY("Tuesday"), WEDNESDAY("Wednesday"), THURSDAY("Thursday"), FRIDAY("Friday"), SATURDAY("Saturday"), SUNDAY("Sunday") } fun main () { for (day in Day.values()) println(day.printableName) println(Day.valueOf("MONDAY").printableName) }
When run, the following result would be printed:
Monday Tuesday Wednesday Thursday Friday Saturday Sunday Monday
Note that the same result can be obtained by employing the two following Kotlin global functions: enumValues<T>()
and enumValueOf<T>()
. They allow you to access any enum class T
with a generic-based approach.
Finally, both use cases can be combined to iterate through them thanks the values()
synthetic method and execute different actions based on their value with a when
expression. Let’s look at an example based on this approach:
enum class Day(val printableName: String) { MONDAY("Monday"), TUESDAY("Tuesday"), WEDNESDAY("Wednesday"), THURSDAY("Thursday"), FRIDAY("Friday"), SATURDAY("Saturday"), SUNDAY("Sunday") } fun main () { for (day in Day.values()) { // common behavior println(day.printableName) // action execute based on day value when (day) { Day.MONDAY -> work() Day.TUESDAY -> work() Day.WEDNESDAY -> work() Day.THURSDAY -> work() Day.FRIDAY -> work() Day.SATURDAY -> rest() Day.SUNDAY -> rest() } // common behavior println("---") } } fun work() { println("Working") } fun rest() { println("Resting") }
This way, custom logic can be executed based on each of the current possible values of which the enum class consists of. If launched, the snippet would return this:
Monday Working --- Tuesday Working --- Wednesday Working --- Thursday Working --- Friday Working --- Saturday Resting --- Sunday Resting ---
In this article, we looked at what Kotlin enum classes are, when and how to use them, and why. As shown, Kotlin enums come with many features and offer you endless possibilities. So, simply thinking of them as a set of constants would be a mistake, as opposed to what happens in many other programming languages.
Since Kotlin enums are classes, they can have their own properties, methods, and implement interfaces. Plus, when used correctly, they can make your code clearer, more readable, and less error-prone. This is why every Kotlin developer should use them, and teaching everything required to do it properly was what this article was about.
Thanks for reading! I hope that you found this article helpful. Feel free to reach out to me with any questions, comments, or suggestions.
LogRocket is an Android monitoring solution that helps you reproduce issues instantly, prioritize bugs, and understand performance in your Android apps.
LogRocket also helps you increase conversion rates and product usage by showing you exactly how users are interacting with your app. LogRocket's product analytics features surface the reasons why users don't complete a particular flow or don't adopt a new feature.
Start proactively monitoring your Android apps — try LogRocket for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowCompare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.