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
- Declaring an enum of named constants
- Why enums beat String / int constants
- Comparing enums safely with ==
- name(), ordinal(), and values()
- Using enums in a switch
- Enums with fields, constructors, and methods
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 runtimeNothing 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
| Method | Returns | Example |
|---|---|---|
| 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
- ❌ Qualifying cases in a switch: write case RED, not case TrafficLight.RED — the latter is a compile error.
- ❌ Depending on ordinal(): reordering constants shifts every ordinal. Switch on the constant or store name().
- ❌ valueOf with a bad name: Day.valueOf("funday") throws IllegalArgumentException; the text must match a constant exactly (case-sensitive).
- ❌ Mutable enum fields: enum data should be final and immutable; constants are shared everywhere.
- ❌ Missing semicolon before members: when an enum has fields/methods, the constant list must end with a ;.
📋 Quick Reference
| Syntax / Method | Description | Example → result |
|---|---|---|
| enum Color { RED, GREEN, BLUE } | Declare a fixed set of constants | defines the type |
| Color.RED | Reference a constant | RED |
| name() | Constant's name as a String | "RED" |
| ordinal() | 0-based position | BLUE.ordinal() → 2 |
| values() | Array of all constants | [RED, GREEN, BLUE] |
| values().length | Count of constants | 3 |
| valueOf("RED") | Constant by exact name | Color.RED |
| valueOf("bad") | Name with no match | throws IllegalArgumentException |
| a == b | Safe comparison (singletons) | RED == RED → true |
| compareTo(other) | Compare by ordinal | BLUE.compareTo(RED) → 2 |
| for (Color c : Color.values()) | Loop over every constant | visits RED, GREEN, BLUE |
| case RED -> ... | Switch case (bare name only) | not case Color.RED |
| RED(255, 0, 0) | Constant with constructor data | enum can hold fields |
| RED, GREEN, BLUE; | Semicolon ends the list when members follow | required 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
- Previous: Checkpoint: Java Basics
- Next: Switch Statement & Expressions — Classic switch plus modern switch expressions and arrow labels
- Quick reference: Java cheat sheet
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.