Enums, Records & Sealed Types

Enum Basics

15 min Lesson 1 of 13

Enum Basics

Before enums arrived in Java 5, developers represented a fixed set of related constants using public static final int fields — the so-called int enum pattern. It worked, but it was fragile: nothing stopped you from passing an arbitrary integer where only a specific set of values made sense, and the values had no type safety whatsoever.

The enum keyword solves all of that. An enum declares a named type whose legal values are a fixed set of constants you enumerate up-front. The compiler then enforces that only those values can ever inhabit a variable of that type.

Declaring an Enum

The syntax looks like a class declaration, but uses the enum keyword:

public enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }

Each identifier in the body — MONDAY, TUESDAY, and so on — is a constant of type Day. By convention, enum constants are written in UPPER_SNAKE_CASE, exactly like other constants in Java.

Enums are full classes. Under the hood, enum Day compiles into a class that extends java.lang.Enum<Day>. Each constant is a public static final Day field of that class. This means enums can have constructors, fields, and methods — topics covered in later lessons.

Using Enum Constants

You reference a constant with the type name and a dot, just like a static field:

Day today = Day.WEDNESDAY; System.out.println(today); // WEDNESDAY System.out.println(today.name()); // WEDNESDAY (inherited from Enum) System.out.println(today.ordinal()); // 2 (zero-based position)

name() returns the constant name as a String. ordinal() returns its zero-based position in the declaration order. Both are inherited from the base class java.lang.Enum.

Do not rely on ordinal() for logic. The ordinal is just a declaration-order index. If someone reorders or inserts constants in the future, every ordinal shifts, silently breaking any code that stored or compared ordinals. Use the constant name or a custom field instead.

Iterating All Constants

Every enum automatically gets a static values() method that returns an array of all constants in declaration order:

for (Day day : Day.values()) { System.out.println(day); } // MONDAY // TUESDAY // WEDNESDAY // THURSDAY // FRIDAY // SATURDAY // SUNDAY

There is also a static valueOf(String name) method that does the reverse lookup:

Day d = Day.valueOf("FRIDAY"); System.out.println(d == Day.FRIDAY); // true

valueOf throws IllegalArgumentException if the string does not match any constant name exactly (case-sensitive).

Enums in switch

One of the most natural uses of an enum is as the selector in a switch statement. Because the compiler knows every possible value of the enum, it can warn you if you forget a case, and the code reads very clearly:

Day today = Day.SATURDAY; switch (today) { case MONDAY: case TUESDAY: case WEDNESDAY: case THURSDAY: case FRIDAY: System.out.println("Weekday — time to work!"); break; case SATURDAY: case SUNDAY: System.out.println("Weekend — enjoy your rest."); break; }

Notice that inside the case labels you write only the constant nameMONDAY, not Day.MONDAY. The compiler already knows the type from the switch selector.

Java 14+ introduced switch expressions, which are cleaner and force you to cover every case (or provide a default):

String kind = switch (today) { case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Weekday"; case SATURDAY, SUNDAY -> "Weekend"; }; System.out.println(today + " is a " + kind); // SATURDAY is a Weekend

The arrow form (->) eliminates fall-through and the need for break. When every constant is covered and there is no default, the compiler enforces exhaustiveness — if you add a new constant to the enum later, the switch expression will refuse to compile until you handle the new case. That is a powerful safety guarantee.

Prefer switch expressions over switch statements when working with enums. The exhaustiveness check catches missing cases at compile time rather than at runtime.

Equality and Comparison

Because each constant is a singleton — exactly one object exists for each constant — you can safely compare enum values with == instead of .equals():

Day a = Day.MONDAY; Day b = Day.valueOf("MONDAY"); System.out.println(a == b); // true — same object System.out.println(a.equals(b)); // true — also works

Enum constants are also Comparable (their natural order is declaration order), and they are Serializable by default.

Summary

Enums replace fragile constant patterns with a type-safe named set. The core ideas to remember: declare with enum, reference constants as Type.CONSTANT, iterate with values(), convert from string with valueOf(), use in switch for readable branching (and prefer the switch-expression form for exhaustiveness), and compare with ==. The next lesson extends enums with custom fields and methods.