1. Previous: Chapter 4
  2. Next: Chapter 6
Kotlin: An Illustrated Guide • Chapter 5

Enum Classes in Kotlin

Chapter cover image
Yes, the last chapter also had a drawing of dogs...

In the last chapter, we created our very own type, called Circle, using a feature in Kotlin called a class.

In this chapter, we’re going to look at a special kind of class—an enum class—which is particularly useful when we want to represent a limited number of values.

I hope you like Schnauzers, because this chapter is full of them!

Limiting the Values

Schnauzers are amazing dogs—they’re smart, they’re loyal to their owners, and they seem to have an opinion about everything!

Let’s create a String variable to hold the name of a pet Schnauzer.

val nameOfSchnauzer: String = "Shadow"

How many different names for Schnauzers can you think of? Shadow, Rover, and Captain Fluffybeard come to mind, but there’s really no limit to the variety of names that could be given to them. The possibilities are unlimited!

Schnauzer breeds, however, are a different story! There are only 3 breeds of Schnauzer.

  1. Miniature Schnauzer
  2. Standard Schnauzer
  3. Giant Schnauzer

So, while the number of names for a Schnauzer is unlimited, the number of breeds is limited to just 3 options.

How does all of this relate to programming?

Well, when we’re writing code, we can reduce the likelihood of errors by choosing a type that limits the range of possible values to only those that are valid.

For example, a String can hold practically any text you can imagine. Since you can name your dog almost anything imaginable, it would make sense to use a String to hold its name.

On the other hand, a String would not be a great choice to hold the specific breed of a Schnauzer. Why? Because strings can hold an infinite number of different values, but there are only 3 possibilities that could be correct for a Schnauzer breed. In other words, when using a String, there’s no way to guarantee that we will set a breed variable to one of those three correct values.

To demonstrate this, let’s say we decided to use a String to represent the breed, like this.

val breedOfSchnauzer: String = "Miniature"

Now, somewhere else in our code, we might have a conditional that’s checking the breed.

if (breedOfSchnauzer == "Mini") {
    // ... do something
}

We might have intended that the conditional expression would evaluate to true, but since we incorrectly typed "Mini" instead of "Miniature", it would have evaluated to false… and we wouldn’t have known that anything was wrong until we ran the code!

The problem is that we used a type that allows unlimited values (that is, the String type), when we needed a type that allows only a limited number of values. If we could create a type that only allows one of the three breed names, the error above wouldn’t even be possible!

To create a type with limited values in Kotlin, we use an enumeration class, or as we more often refer to it, an enum class, or just an enum.

Creating an Enum Class

Let’s create an enum class to represent Schnauzer breeds.

enum class SchnauzerBreed {
    MINIATURE,
    STANDARD,
    GIANT
}

This enum class is named SchnauzerBreed, and it gives us three breed options to choose from. Here are the main parts of an enum class.

enum and class are keywords, SchnauzerBreed is the class name, and MINIATURE, STANDARD, and GIANT are the enum entries. enum class SchnauzerBreed { MINIATURE , STANDARD , GIANT } keywords enum entries class name
  1. To create an enum class, we write enum class rather than just class.

    1. After that comes the name that we want to give our class—in this case, SchnauzerBreed.

    2. Inside the body of the class, the options are called enum entries. Sometimes we call them enum constants instead. They’re separated with commas, and by convention, they’re written in all capital letters.

Using an Enum Class

Now that we have an enum class, we can start using it. Note that we will not construct an object from an enum class in the same way that we would do with normal classes. If we try, we’ll get an error.

val breed: SchnauzerBreed = SchnauzerBreed()
Error

Instead, we just assign a variable to one of the entries, like this.

val breed: SchnauzerBreed = SchnauzerBreed.GIANT

By using an enum class here, if we were to accidentally type MINI instead of MINIATURE, then Kotlin would give us an error immediately—before we even run the code!

val breed: SchnauzerBreed = SchnauzerBreed.MINI
Error

So instead of using a String, which can be set to just about anything, we limited the valid options to just three enum entries. By doing that, Kotlin can now help tell us when we typed something wrong, without having to even run the code!

Using Enum Classes with when Expressions

Cartoon of a Miniature Schnauzer

Another nice benefit of using enum classes is that Kotlin provides some added assistance when we use them with a when expression.

As you might recall, when expressions must account for every condition—that is, they must be exhaustive. Because enum classes limit the possible values, Kotlin can use this information to know when we have indeed accounted for every condition.

For example, here’s some code that returns a description of a breed.

fun describe(breed: SchnauzerBreed) = when (breed) {
    SchnauzerBreed.MINIATURE -> "Small"
    SchnauzerBreed.STANDARD  -> "Medium"
    SchnauzerBreed.GIANT     -> "Large"
}

Notice that there’s no else condition here! Kotlin can tell that we’ve included all three of the enum entries in the when body, so there are no other possibilities.

If we were to omit some of the entries, Kotlin would give us an error.

fun describe(breed: SchnauzerBreed) = when (breed) {
    SchnauzerBreed.MINIATURE -> "Small"
    SchnauzerBreed.STANDARD  -> "Medium"
}
Error

To remedy this error, we would either have to provide all of the enum constants, as we did in Listing 5.8, or we’d have to provide an else condition, like this.

fun describe(breed: SchnauzerBreed) = when (breed) {
    SchnauzerBreed.MINIATURE -> "Small"
    SchnauzerBreed.STANDARD  -> "Medium"
    else                     -> "Unknown"
}

It’s great that Kotlin will give us an error when our when is not exhaustive. For example, whenever we add a new entry to an existing enum class, Kotlin will give us errors in all of the when expressions that need to be updated—so we can know exactly what parts of our code need to be fixed!

Adding Properties and Functions to Enum Classes

Cartoon of a Standard Schnauzer

As you recall from the last chapter, normal Kotlin classes allow us to put properties and functions together. Well, it’s also possible to add properties and functions to an enum class.

Let’s start by adding a property as a constructor parameter. We can do this just as we did with normal classes—by putting them between the opening ( and closing ) parentheses after the name of the class.

For example, we might want to include the approximate height of each breed, in centimeters. We can do that like this.

enum class SchnauzerBreed(val height: Int) {
    MINIATURE(33),
    STANDARD(47),
    GIANT(65)
}

Because we added a new constructor parameter called height, we also had to add a constructor argument to each of the enum entries. So a miniature has an approximate height of 33 cm, a standard is about 47 cm, and a giant is about 65 cm tall.

height is a constructor parameter, and 33, 47, and 65 are the constructor arguments. enum class SchnauzerBreed( val height : Int) { MINIATURE ( 33 ) , STANDARD ( 47 ) , GIANT ( 65 ) } constructor arguments constructor parameter

Note that the enum entries are in fact instances of the enum class.

We can get a property off of an enum instance just like we would do with regular objects. For example, we can print the height of a breed to the screen with println().

println(SchnauzerBreed.MINIATURE.height)

We can also include a property that does not require a constructor argument. Let’s add a property that tells us the family of breeds that they all belong to.

enum class SchnauzerBreed(val height: Int) {
    MINIATURE(33),
    STANDARD(47),
    GIANT(65);

    val family: String = "Schnauzer"
}

The new family property is added to the end of the class body. Notice the semicolon after the last entry!

It’s just as easy to add a function. As above, be sure to include the semicolon, and then simply write the function beneath the enum entries.

enum class SchnauzerBreed(val height: Int) {
    MINIATURE(33),
    STANDARD(47),
    GIANT(65);

    val family: String = "Schnauzer"

    fun isShorterThan(centimeters: Int) = height < centimeters
}

When using an enum class, we can get properties and call functions just as we would with any other class.

println(SchnauzerBreed.STANDARD.family)
println(SchnauzerBreed.STANDARD.isShorterThan(40))

Built-In Properties

Cartoon of a Giant Schnauzer

In addition to any properties that you include yourself, Kotlin also automatically adds a couple of properties to all enum instances. Before we wrap up this chapter, let’s take a look at those.

ordinal

Each enum entry has a property named ordinal, which tells us its position—~where this enum entry appears in the list_. The first entry in the list has an ordinal of 0, the second has an ordinal of 1, the third has an ordinal of 2, and so on. For example, since STANDARD is the second entry in the list, it has an ordinal of 1.

MINIATURE has ordinal 0, STANDARD has ordinal 1, and GIANT has ordinal 2. enum class SchnauzerBreed( val height : Int) { MINIATURE ( 33 ) , STANDARD ( 47 ) , GIANT ( 65 ) } 0 1 2

Yes, it starts with zero, which can be a bit confusing for those who haven’t done much programming before. Once we get to the topic of collections in Chapter 8, we’ll see how zero-based numbering is used a lot in programming!

name

When we need to know the name of the enum entry as a string, we can use the name property. To demonstrate this, let’s create a function that tells us the name of the breed, along with the height.

fun describe(breed: SchnauzerBreed) {
    println(breed.name)
    println(breed.height)
}

Now, we can call describe(SchnauzerBreed.STANDARD), and we’ll see this on the screen.

STANDARD
47

There’s more than this! We can also get all of the entries out of an enum class. However, this won’t be helpful until we learn how to iterate over values, which we’ll cover in Chapter 8. For now, let’s wrap up this chapter!

Summary

Enjoying this book?
Order the paperback today!

Kotlin: An Illustrated Guide is now available on Amazon See the book on Amazon

Here’s what we learned in this chapter:

At this point, we’ve created lots of variables, each one holding a value. But what about when we need to handle the absence of a value? In the next chapter, we’ll learn about nulls and the tools that Kotlin provides to handle them safely!

Thanks to James Lorenzen and Mohit for reviewing this chapter!

Share this article:

  • Share on Facebook
  • Share on Reddit