Enums

Reviewed & published by Brayan K

Some values come from a small, fixed set: the days of the week, the suits in a deck, a traffic light's colour. An enum turns that set into its own type, so the compiler guarantees a value is always one of the allowed options — never a typo'd String or a meaningless int.

Part of the free Java course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

What You'll Learn in This Lesson

1️⃣ The Problem Enums Solve

Imagine representing a traffic light with a String:

String light = "red";
String light2 = "Red";    // oops — different capitalisation
String light3 = "geen";   // oops — typo, compiles fine, breaks at runtime

Nothing stops you from assigning "banana". The bug only appears when the program runs. An enum closes that hole — the value can only be one of the constants you declared, and the compiler catches anything else:

enum TrafficLight { RED, YELLOW, GREEN }

TrafficLight light = TrafficLight.RED;     // ✅
TrafficLight bad   = TrafficLight.PURPLE;  // ❌ won't compile

💡 Analogy: A String is a blank form where anyone can scribble anything. An enum is a drop-down menu — the user can only pick from the options you provided.

public class Main {
    // An enum defines a FIXED set of named constants.
    // Days of the week can ONLY be one of these seven.
    enum Day {
        MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
    }

    public static void main(String[] args) {
        Day today = Day.WEDNESDAY;
        System.out.println("Today is " + today);          // WEDNESDAY

        // Compare enums safely with == (each constant is a singleton)
        boolean isFriday = today == Day.FRIDAY;
        System.out.println("Is it Friday? " + isFriday);

        // Every enum value knows its name() and its ordinal() (position)
        System.out.println("name():    " + today.name());      // WEDNESDAY
        System.out.println("ordinal(): " + today.ordinal());   // 2 (0-based)

        // values() returns every constant — great for looping
        for (Day d : Day.values()) {
            System.out.println(d.ordinal() + " = " + d);
        }
    }
}

2️⃣ The Built-in Methods Every Enum Has

MethodReturnsExample
name()The constant's name (String)"WEDNESDAY"
ordinal()0-based position (int)2
values()Array of all constants[MONDAY, ...]
valueOf("X")The constant named "X"Day.valueOf("FRIDAY")

3️⃣ Enums + switch = a Perfect Match

The natural way to act on an enum is a switch. Inside a switch over an enum you write the bare constant name — case RED, not case TrafficLight.RED. The compiler can even warn you if you forget a case.

String action = switch (light) {
    case RED    -> "Stop";
    case YELLOW -> "Slow down";
    case GREEN  -> "Go";
};

This arrow form is a switch expression — it produces a value you can assign directly. You'll study switch fully in the next lesson; enums are its ideal companion.

public class Main {
    enum TrafficLight { RED, YELLOW, GREEN }

    public static void main(String[] args) {
        for (TrafficLight light : TrafficLight.values()) {
            String action = switch (light) {
                case RED    -> "Stop";
                case YELLOW -> "Slow down";
                case GREEN  -> "Go";
            };
            System.out.println(light + " -> " + action);
        }
    }
}

4️⃣ Enums With Data and Behaviour

This is where enums become genuinely powerful. Each constant can carry its own data, supplied through a constructor, and the enum can define methods shared by every constant. A classic example is planets, each with a mass and radius and a method that computes surface gravity.

The constructor runs once per constant when the enum is first used. The fields are typically final so each constant's data never changes — enums should be immutable.

public class Main {
    // Enums can hold DATA and have METHODS, not just be plain labels.
    enum Planet {
        MERCURY(3.30e23, 2.44e6),
        EARTH(5.97e24, 6.37e6),
        JUPITER(1.90e27, 6.99e7);

        private final double mass;     // kilograms
        private final double radius;   // metres

        // The constructor runs once per constant
        Planet(double mass, double radius) {
            this.mass = mass;
            this.radius = radius;
        }

        // A method shared by every constant
        double surfaceGravity() {
            final double G = 6.674e-11;
            return G * mass / (radius * radius);
        }
    }

    public static void main(String[] args) {
        for (Planet p : Planet.values()) {
            System.out.printf("%-8s gravity = %.2f m/s^2%n",
                              p, p.surfaceGravity());
        }
    }
}
public class Main {
    // 1) A fixed set of named values — not a String, not an int.
    ___ Status {                                     // 👉 replace ___ with enum
        DRAFT("not sent yet"),
        SENT("waiting for payment"),
        PAID("done");

        private final String note;

        // 2) The constructor has the enum's own name. It is always private.
        ___(String note) { this.note = note; }       // 👉 replace ___ with Status

        String note() { return note; }

        // 3) Compare enum constants with ==. They are singletons.
        boolean isFinished() { return this ___ PAID; }   // 👉 replace ___ with ==
    }

    public static void main(String[] args) {
        Status s = Status.SENT;

        // 4) The constant's own text, exactly as it is written above.
        System.out.println("Name:     " + s.___());  // 👉 replace ___ with name
        System.out.println("Position: " + s.ordinal());
        System.out.println("Note:     " + s.note());
        System.out.println("Finished? " + s.isFinished());

        // 5) Every constant, in the order they are declared.
        for (Status v : Status.___()) {              // 👉 replace ___ with values
            System.out.println("  " + v + " -> " + v.note());
        }

        Status parsed = Status.valueOf("PAID");
        switch (parsed) {
            case DRAFT -> System.out.println("Still editing");
            case SENT  -> System.out.println("Chasing payment");
            case PAID  -> System.out.println("Nothing to do");
        }
    }
}

🧩 Reorder Challenge

Reorder these lines so the program declares a Size enum and prints the ordinal of LARGE.

Size pick = Size.LARGE;
enum Size { SMALL, MEDIUM, LARGE }
System.out.println(pick.ordinal());
enum Size { SMALL, MEDIUM, LARGE }
Size pick = Size.LARGE;
System.out.println(pick.ordinal());

Why: The enum type must be declared before a variable of that type can exist, and pick must be assigned before you read its ordinal(). LARGE is the third constant, so the output is 2 (ordinals are 0-based).

🧠 Quick Recall

1. What does this print?

enum Color { RED, GREEN, BLUE }
System.out.println(Color.BLUE.ordinal());

2. Ordinals are 0-based: RED=0, GREEN=1, BLUE=2.

2. What does this print?

enum Color { RED, GREEN, BLUE }
System.out.println(Color.values().length);

3. values() returns an array of all three constants, so its length is 3.

3. Is Color.RED == Color.RED a safe comparison?

Yes — it prints true. Each enum constant is a singleton, so == works correctly (and is null-safe), unlike with Strings.

Common Beginner Mistakes

📋 Quick Reference

Syntax / MethodDescriptionExample → result
enum Color { RED, GREEN, BLUE }Declare a fixed set of constantsdefines the type
Color.REDReference a constantRED
name()Constant's name as a String"RED"
ordinal()0-based positionBLUE.ordinal() → 2
values()Array of all constants[RED, GREEN, BLUE]
values().lengthCount of constants3
valueOf("RED")Constant by exact nameColor.RED
valueOf("bad")Name with no matchthrows IllegalArgumentException
a == bSafe comparison (singletons)RED == RED → true
compareTo(other)Compare by ordinalBLUE.compareTo(RED) → 2
for (Color c : Color.values())Loop over every constantvisits RED, GREEN, BLUE
case RED -> ...Switch case (bare name only)not case Color.RED
RED(255, 0, 0)Constant with constructor dataenum can hold fields
RED, GREEN, BLUE;Semicolon ends the list when members followrequired before fields/methods

🎉 Lesson Complete!

You can now model a fixed set of options as a type-safe enum: declare constants, compare them with ==, use name()/ordinal()/values(), drive a switch with them, and even attach data and methods to each constant.

Practice quiz

What is an enum?

  • A resizable list
  • A key-value store
  • A type whose value is one of a fixed set of named constants
  • A kind of loop

Answer: A type whose value is one of a fixed set of named constants. An enum defines a fixed, named set of constants the value can only be one of.

What does ordinal() return for an enum constant?

  • Its 0-based position in declaration order
  • Its name as text
  • Its 1-based position
  • Its hash code

Answer: Its 0-based position in declaration order. ordinal() is the zero-based position in declaration order.

For enum Color { RED, GREEN, BLUE }, what is Color.BLUE.ordinal()?

  • 1
  • 3
  • 0
  • 2

Answer: 2. Ordinals are 0-based: RED=0, GREEN=1, BLUE=2.

What does values() return?

  • The number of constants
  • An array of all the enum's constants
  • The first constant
  • A sorted list of names

Answer: An array of all the enum's constants. values() returns an array of every constant, useful for looping.

For enum Color { RED, GREEN, BLUE }, what is Color.values().length?

  • 3
  • 2
  • 0
  • 1

Answer: 3. There are three constants, so the array length is 3.

Is comparing enums with == safe?

  • No, use equals()
  • Only for the first constant
  • Yes — each constant is a singleton and == is null-safe
  • Only inside a switch

Answer: Yes — each constant is a singleton and == is null-safe. Each enum constant is a single shared instance, so == works correctly and is null-safe.

Inside a switch over an enum, how do you write a case label?

  • case TrafficLight.RED
  • case RED
  • case "RED"
  • case RED()

Answer: case RED. Use the bare constant name (case RED); qualifying it is a compile error.

Can an enum have fields, a constructor, and methods?

  • No, only constants
  • Only static methods
  • Only if it implements an interface
  • Yes — each constant can carry data and the enum can define methods

Answer: Yes — each constant can carry data and the enum can define methods. Enums can have fields passed to a constructor and methods shared by all constants (e.g. Planet).

What does Day.valueOf("funday") do when no such constant exists?

  • Returns null
  • Throws IllegalArgumentException
  • Returns the first constant
  • Creates a new constant

Answer: Throws IllegalArgumentException. valueOf throws IllegalArgumentException if the name does not match a constant exactly.

When an enum has fields or methods, what must follow the list of constants?

  • A comma
  • Nothing
  • A semicolon (;)
  • The 'end' keyword

Answer: A semicolon (;). The constant list must end with a semicolon before any fields or methods.

Continue this course

Frequently asked questions

What is an enum and why not just use String or int constants?

An enum is a special type whose value can only be one of a fixed, named set of constants, such as RED, YELLOW, GREEN. Using an enum instead of magic Strings or int codes means the compiler enforces valid values — you literally cannot pass PURPLE where a TrafficLight is expected — and you get readable names, autocompletion, and exhaustive switch checking for free.

Can I compare enums with == or do I need equals()?

You can safely use == with enums. Each enum constant is a single shared instance (a singleton), so == compares identity and gives the right answer, and unlike Strings it never has the surprising pooling behaviour. == is also null-safe: it won't throw if the variable is null, whereas calling equals on null would.

What do name() and ordinal() return?

name() returns the exact constant name as a String, like "WEDNESDAY". ordinal() returns the zero-based position in the declaration order, so the first constant is 0. Avoid relying on ordinal() for logic or storage, because inserting or reordering constants silently changes the numbers; store name() instead if you must persist an enum value.

Can an enum have fields, a constructor, and methods?

Yes. Each constant can carry data passed to a private constructor (for example Planet(mass, radius)), and the enum can declare methods that every constant shares (like surfaceGravity()). This makes enums far more powerful than simple labels — they can bundle related data and behaviour into a tidy, type-safe set.

Why are enums great with switch?

A switch over an enum is clean and the compiler can warn you when you forget a case, which is impossible with arbitrary Strings or ints. With the arrow form, switch (light) { case RED -> ...; } reads almost like English and returns a value directly. You'll explore switch in depth in the next lesson; enums are its perfect partner.