1. Overview

In Kotlin, enum is a convenient tool for defining a fixed set of values. They provide a handy way to represent a collection of related constants.

When working with enums, we sometimes need to programmatically retrieve all the entries’ names.

In this tutorial, we’ll explore how to achieve this using generic functions in Kotlin.

2. Introduction to the Problem

Generics allow us to create reusable code that works with any enum type.

First, let’s create two different enums:

enum class CountryCode(countryName: String) {
    USA("United States of America"),
    UKR("Ukraine"),
    CAN("Canada"),
    MEX("Mexico"),
    JAM("Jamaica")
}

enum class WorkingDay {
    Monday, Tuesday, Wednesday, Thursday, Friday
}

As we can see, we’ve created a simple enum WorkingDay and a CountryCode enum with a property. We aim to write a generic function to obtain the names of all the entries in the enum as a list. Of course, the function should work for all enum types.

For simplicity, we’ll take CountryCode and WorkingDay as inputs and use unit test assertions to verify whether each approach works as expected.

Next, let’s dive in and see how it’s done!

3.  Using Java’s Class.getEnumConstants() Function

In the Java standard library, the java.lang.Class class provides the getEnumConstant() method that gives us an array containing the values comprising the enum class.

So, we can get our enum’s Java Class object, get the enum entry array, and extract entry names from the array:

val names = WorkingDay::class.java.enumConstants.map { it.name }
assertEquals(listOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday"), names)

As we can see, we use the map() function to convert the enum entry array to a list of strings (entry names).

Next, we need to create a generic function so that it works for all enums:

fun getNames1(enumCls: Class<out Enum<*>>) = enumCls.enumConstants.map(Enum<*>::name)

The getNames1() function above expects a Java Class object as the parameter and returns all enum entry names in a list. Of course, the Java Class object must be an enum class.

Finally, if we test the getName1() function with our two enums, the test passes:

val dayNames = getNames1(WorkingDay::class.java)
assertEquals(listOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday"), dayNames)

val codeNames = getNames1(CountryCode::class.java)
assertEquals(listOf("USA", "UKR", "CAN", "MEX", "JAM"), codeNames)

4. Using the enumValues() Function

We just solved the problem using the Java Class object’s getEnumConstants() method. Alternatively, Kotlin provides the reified enumValues() function to get all entries of Enum<T>:

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

The reified keyword allows us to access the type information of T at runtime. Therefore, we can create a function based on enumValues() to get all entry names of  an enum:

inline fun <reified T : Enum<T>> getNames2() = enumValues<T>().map { it.name }

In the code above, we made the function an inline function to instruct the compiler to inline the function’s code at the call site, which can improve performance by avoiding the function call overhead.

If we verify the getNames2() function with our two enums, the function works as expected:

val dayNames = getNames2<WorkingDay>()
assertEquals(listOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday"), dayNames)

val codeNames = getNames2<CountryCode>()
assertEquals(listOf("USA", "UKR", "CAN", "MEX", "JAM"), codeNames)

5. Using the values() Function and the entries Property

So far, we’ve created the getNames1() and getNames2() functions to get all entry names of an enum. They work as expected. However, when we want to call a function to get all entry names of an enum, we usually write the enum type first, for example, WorkingDay.something.someFunction(). This can be more natural and fluent compared to “getNames1(WorkingDay::class.java)” or “getNames2<WorkingDay>()”.

So next, let’s see if we can create a more fluent function to solve the problem.

5.1. The value() Function

We know all enum classes ship with the values() function, which returns all enum entries in an array. We’ve also learned that we can convert an array of enum entries to a list of entry names.

Therefore, we can create an extension function on the enum entry array object to get the entry names quickly:

fun <T : Enum<T>> Array<T>.names() = this.map { it.name }

As we can see up there, we use the type parameter T to restrict that this extension is only available for enum entry arrays.

Now, with this extension, we can write in a fluent style to get enum entry names:

val dayNames = WorkingDay.values().names()
assertEquals(listOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday"), dayNames)

val codeNames = CountryCode.values().names()
assertEquals(listOf("USA", "UKR", "CAN", "MEX", "JAM"), codeNames)

5.2. The entries Property

We’ve talked about the enum.values() function. In Kotlin 1.8.20, the JetBrains team introduced the entries property for enums as an experiment to replace the values() function since the team detected a hidden performance issue when using an enum array in Kotlin and Java.

Since version 1.9.0, the entries property feature has become stable. So, for example, WorkingDay.entries gives us all entries in a list.

Therefore, if we’re using Kotlin 1.9.0, we can change our extension function to extend EnumEntries:

fun <T : Enum<T>> EnumEntries<T>.names() = this.map { it.name }

As the code shows, the logic is basically the same as the array extension.

Finally, let’s see how to use the extension in a test:

val dayNames = WorkingDay.entries.names()
assertEquals(listOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday"), dayNames)

val codeNames = CountryCode.entries.names()
assertEquals(listOf("USA", "UKR", "CAN", "MEX", "JAM"), codeNames)

6. Conclusion

In this article, we’ve explored different approaches to retrieving enum entry names generically. Also, we’ve covered some essential concepts in Kotlin, such as inline functions, extension functions, generics, etc.

As usual, all code snippets presented here are available over on GitHub.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.