# Enumerations

An enumeration is a collection of names.

Kotlin’s enum class is a convenient way to manage these names:

// Enumerations/Level.kt
package enumerations
import atomictest.eq

enum class Level {
  Overflow, High, Medium, Low, Empty
}

fun main() {
  Level.Medium eq "Medium"
}

Creating an enum generates toString()s for the enum names.

You must qualify each reference to an enumeration name, as with Level.Medium in main(). You can eliminate this qualification using an import to bring all names from the enumeration into the current namespace (namespaces keep names from colliding with each other):

// Enumerations/EnumImport.kt
import atomictest.eq
import enumerations.Level.*   // [1]

fun main() {
  Overflow eq "Overflow"
  High eq "High"
}
  • [1] The * imports all the names inside the Level enumeration, but does not import the name Level.

You can import enum values into the same file where the enum class is defined:

// Enumerations/RecursiveEnumImport.kt
package enumerations
import atomictest.eq
import enumerations.Size.*            // [1]

enum class Size {
  Tiny, Small, Medium, Large, Huge, Gigantic
}

fun main() {
  Gigantic eq "Gigantic"              // [2]
  Size.values().toList() eq           // [3]
    listOf(Tiny, Small, Medium,
      Large, Huge, Gigantic)
  Tiny.ordinal eq 0                   // [4]
  Huge.ordinal eq 4
}
  • [1] We import the values of Size before the Size definition appears in the file.
  • [2] After the import, we no longer need to qualify access to the enumeration names.
  • [3] You can iterate through the enumeration names using values(). values() returns an Array, so we call toList() to convert it to a List.
  • [4] The first declared constant of an enum has an ordinal value of zero. Each subsequent constant receives the next integer value.

You can perform different actions for different enum entries using a when expression. Here we import the name Level, as well as all its entries:

// Enumerations/CheckingOptions.kt
package checkingoptions
import atomictest.*
import enumerations.Level
import enumerations.Level.*

fun checkLevel(level: Level) {
  when (level) {
    Overflow -> trace(">>> Overflow!")
    Empty -> trace("Alert: Empty")
    else -> trace("Level $level OK")
  }
}

fun main() {
  checkLevel(Empty)
  checkLevel(Low)
  checkLevel(Overflow)
  trace eq """
    Alert: Empty
    Level Low OK
    >>> Overflow!
  """
}

checkLevel() performs specific actions for only two of the constants, while behaving ordinarily (the else case) for all other options.

An enumeration is a special kind of class with a fixed number of instances, all listed within the class body. In other ways, an enum class behaves like a regular class, so you can define member properties and functions. If you include additional elements, you must add a semicolon after the last enumeration value:

// Enumerations/Direction.kt
package enumerations
import atomictest.eq
import enumerations.Direction.*

enum class Direction(val notation: String) {
  North("N"), South("S"),
  East("E"), West("W");  // Semicolon required
  val opposite: Direction
    get() = when (this) {
      North -> South
      South -> North
      West -> East
      East -> West
    }
}

fun main() {
  North.notation eq "N"
  North.opposite eq South
  West.opposite.opposite eq West
  North.opposite.notation eq "S"
}

The Direction class contains a notation property holding a different value for each instance. You pass values for the notation constructor parameter in parentheses (North("N")), just like you construct an instance of a regular class.

The getter for the opposite property dynamically computes the result when it is accessed.

Notice that when doesn’t require an else branch in this example, because all possible enum entries are covered.

  • -

Enumerations can make your code more readable, which is always desirable.

Exercises and solutions can be found at www.AtomicKotlin.com.