Skip to content
Java oops misc 5 min read

Enums

Enums (short for enumerations) give you a clean, type-safe way to represent a fixed set of named constants — think days of the week, compass directions, or order statuses. Unlike plain int or String constants, an enum makes illegal values impossible at compile time.

Why Use Enums?

Before enums (introduced in Java 5), developers often wrote constants like this:

// Old pattern — error-prone int flags
public static final int STATUS_NEW      = 0;
public static final int STATUS_ACTIVE   = 1;
public static final int STATUS_INACTIVE = 2;

Nothing stops a caller from passing 99 where only 0–2 make sense. Enums eliminate that entire class of bug by making the type itself the constraint.

Declaring a Basic Enum

Use the enum keyword instead of class:

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

Each identifier (MONDAY, TUESDAY, …) is a constant — an instance of the Day type. By convention, enum constants are written in UPPER_SNAKE_CASE.

Using an Enum

public class Main {
    public static void main(String[] args) {
        Day today = Day.WEDNESDAY;
        System.out.println(today);            // WEDNESDAY
        System.out.println(today.ordinal());  // 2  (zero-based index)
        System.out.println(today.name());     // WEDNESDAY
    }
}

Output:

WEDNESDAY
2
WEDNESDAY

Enums in a switch Statement

Enums pair naturally with switch:

Day day = Day.SATURDAY;

switch (day) {
    case SATURDAY:
    case SUNDAY:
        System.out.println("Weekend!");
        break;
    default:
        System.out.println("Weekday.");
}

Output:

Weekend!

With modern switch expressions (Java 14+), this becomes even cleaner:

String type = switch (day) {
    case SATURDAY, SUNDAY -> "Weekend";
    default               -> "Weekday";
};
System.out.println(type); // Weekend

Built-in Enum Methods

Every enum automatically inherits several useful methods:

MethodDescription
name()Returns the constant’s declared name as a String
ordinal()Returns the zero-based position in the declaration
toString()Same as name() by default (can be overridden)
Day.values()Returns an array of all constants in declaration order
Day.valueOf("MONDAY")Returns the constant matching the given string
// Iterating all constants
for (Day d : Day.values()) {
    System.out.println(d.ordinal() + " -> " + d);
}

Output:

0 -> MONDAY
1 -> TUESDAY
2 -> WEDNESDAY
3 -> THURSDAY
4 -> FRIDAY
5 -> SATURDAY
6 -> SUNDAY

Tip: Use Day.valueOf("FRIDAY") when you need to convert a user-provided string back to an enum. It throws IllegalArgumentException if the string doesn’t match any constant, so wrap it in a try-catch or validate first.

Enums with Fields and Constructors

Here is where enums really shine: each constant can carry its own data.

public enum Planet {
    MERCURY(3.303e+23, 2.4397e6),
    VENUS  (4.869e+24, 6.0518e6),
    EARTH  (5.976e+24, 6.37814e6);

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

    // Enum constructor — always implicitly private
    Planet(double mass, double radius) {
        this.mass   = mass;
        this.radius = radius;
    }

    double surfaceGravity() {
        final double G = 6.67300E-11;
        return G * mass / (radius * radius);
    }
}
System.out.printf("Earth gravity: %.2f m/s²%n", Planet.EARTH.surfaceGravity());

Output:

Earth gravity: 9.80 m/s²

Note: Enum constructors are always private (or package-private, which Java silently makes private). You cannot call new Planet(...) yourself — the JVM creates the instances once, at class-loading time.

Enums with Abstract Methods

You can declare an abstract method inside an enum and override it per constant — a compact form of the Strategy pattern:

public enum Operation {
    ADD {
        @Override
        public int apply(int a, int b) { return a + b; }
    },
    SUBTRACT {
        @Override
        public int apply(int a, int b) { return a - b; }
    },
    MULTIPLY {
        @Override
        public int apply(int a, int b) { return a * b; }
    };

    public abstract int apply(int a, int b);
}
System.out.println(Operation.ADD.apply(10, 3));      // 13
System.out.println(Operation.SUBTRACT.apply(10, 3)); // 7
System.out.println(Operation.MULTIPLY.apply(10, 3)); // 30

Output:

13
7
30

Implementing Interfaces

Enums can implement interfaces — they just cannot extend a class (they already implicitly extend java.lang.Enum).

interface Describable {
    String describe();
}

public enum Season implements Describable {
    SPRING, SUMMER, AUTUMN, WINTER;

    @Override
    public String describe() {
        return "Season: " + name();
    }
}
System.out.println(Season.SUMMER.describe()); // Season: SUMMER

EnumSet and EnumMap

The java.util package ships two highly efficient collections built specifically for enums:

  • EnumSet — a Set backed by a bit vector; blazing fast for membership checks
  • EnumMap — a Map with enum keys; more compact than HashMap
import java.util.EnumSet;

EnumSet<Day> weekend = EnumSet.of(Day.SATURDAY, Day.SUNDAY);
System.out.println(weekend.contains(Day.SATURDAY)); // true
System.out.println(weekend.contains(Day.MONDAY));   // false

See the dedicated EnumSet & EnumMap page for full coverage.

Under the Hood

When the Java compiler encounters an enum, it generates a regular class that:

  1. Extends java.lang.Enum<E> — giving you name(), ordinal(), compareTo(), and equals()/hashCode() for free.
  2. Declares each constant as a public static final field of the enum’s own type, initialized in a static {} block.
  3. Creates a hidden $VALUES array that values() clones and returns.

You can verify this with the javap tool:

javap -p Day.class

The output will show something like:

public static final Day MONDAY;
public static final Day TUESDAY;
...
private static final Day[] $VALUES;
static {};

Because each constant is a singleton created at class-load time, enum-based singletons are thread-safe by default — a popular pattern in effective Java design. Enum serialization is also safe: the JVM guarantees that deserialization returns the existing singleton rather than creating a new instance.

Warning: Rely on name() or an explicit field for persistence (databases, files, APIs) — never on ordinal(). Inserting a new constant in the middle of an enum will shift all subsequent ordinals and silently corrupt stored data.

Comparing Enums

Use == to compare enum values, not .equals(). Because every constant is a singleton, == is both safe and slightly faster:

Day d = Day.FRIDAY;
System.out.println(d == Day.FRIDAY);   // true
System.out.println(d.equals(Day.FRIDAY)); // also true, but == is preferred

Quick Reference

FeatureSupported?
FieldsYes
Constructors (public/protected)No — always private
Instance methodsYes
Abstract methodsYes
Implement interfaceYes
Extend a classNo (already extends java.lang.Enum)
Used in switchYes
SerializableYes (built-in)
  • Switch Expressions — pattern-match over enums with concise arrow syntax in Java 14+
  • EnumSet & EnumMap — high-performance collections designed for enum keys and values
  • Static Keyword — understand why enum constants are implicitly public static final
  • Interfaces — enums can implement interfaces to add polymorphic behavior
  • Serialization — how enum singletons survive object serialization safely
  • Pattern Matching — combine enums with sealed types and pattern matching in Java 21
Last updated June 13, 2026
Was this helpful?