- Basic Enum
- Enum with Fields and Constructor
- Enum Methods
- Enum with Abstract Methods
- Switch on Enum
- EnumSet and EnumMap
- Iterating Enum Values
The simplest enum is a list of named constants. The compiler generates a class that extends
java.lang.Enum, making every constant a singleton instance.
public enum Direction {
NORTH, SOUTH, EAST, WEST
}
public class Main {
public static void main(String[] args) {
Direction d = Direction.NORTH;
// name() returns the declared identifier as a String
System.out.println(d.name()); // NORTH
// ordinal() returns the zero-based position
System.out.println(d.ordinal()); // 0
// valueOf() looks up a constant by its name
Direction east = Direction.valueOf("EAST");
System.out.println(east); // EAST
// == is safe for enums — constants are singletons
System.out.println(d == Direction.NORTH); // true
// toString() defaults to name()
System.out.println(Direction.WEST.toString()); // WEST
}
}
Every enum implicitly implements java.io.Serializable and java.lang.Comparable.
The natural ordering is the declaration order (by ordinal).
Each enum constant can carry data. You add fields and a constructor to the enum body. The constructor is always
implicitly private (enums cannot be instantiated externally), and each constant's arguments are
passed in parentheses after its name.
public enum Planet {
MERCURY(3.303e+23, 2.4397e6),
VENUS (4.869e+24, 6.0518e6),
EARTH (5.976e+24, 6.37814e6),
MARS (6.421e+23, 3.3972e6);
private final double mass; // kg
private final double radius; // metres
// Constructor — always private
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
static final double G = 6.67300E-11;
double surfaceGravity() {
return G * mass / (radius * radius);
}
double surfaceWeight(double otherMass) {
return otherMass * surfaceGravity();
}
}
public class Main {
public static void main(String[] args) {
double earthWeight = 75.0; // kg
double mass = earthWeight / Planet.EARTH.surfaceGravity();
for (Planet p : Planet.values()) {
System.out.printf("Weight on %-10s: %.2f N%n",
p, p.surfaceWeight(mass));
}
// Weight on MERCURY : 28.33 N
// Weight on VENUS : 67.90 N
// Weight on EARTH : 75.00 N
// Weight on MARS : 28.46 N
}
}
// Enum with a display label different from the constant name
public enum HttpStatus {
OK(200, "OK"),
CREATED(201, "Created"),
BAD_REQUEST(400, "Bad Request"),
UNAUTHORIZED(401, "Unauthorized"),
NOT_FOUND(404, "Not Found"),
INTERNAL_SERVER_ERROR(500, "Internal Server Error");
private final int code;
private final String reason;
HttpStatus(int code, String reason) {
this.code = code;
this.reason = reason;
}
public int code() { return code; }
public String reason() { return reason; }
// Reverse lookup — find enum by numeric code
public static HttpStatus fromCode(int code) {
for (HttpStatus s : values()) {
if (s.code == code) return s;
}
throw new IllegalArgumentException("Unknown status code: " + code);
}
@Override
public String toString() {
return code + " " + reason;
}
}
// Usage
System.out.println(HttpStatus.NOT_FOUND); // 404 Not Found
System.out.println(HttpStatus.fromCode(201)); // 201 Created
System.out.println(HttpStatus.OK.code()); // 200
Enums can define any non-abstract method in the enum body. These methods operate on the constant's fields and
can call other methods. You can also override toString() to control how constants are printed.
public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
public boolean isWeekend() {
return this == SATURDAY || this == SUNDAY;
}
public boolean isWeekday() {
return !isWeekend();
}
// The next day, wrapping around
public Day next() {
Day[] values = Day.values();
return values[(this.ordinal() + 1) % values.length];
}
public Day previous() {
Day[] values = Day.values();
int prev = (this.ordinal() - 1 + values.length) % values.length;
return values[prev];
}
}
public class Main {
public static void main(String[] args) {
System.out.println(Day.FRIDAY.isWeekend()); // false
System.out.println(Day.SATURDAY.isWeekend()); // true
System.out.println(Day.SUNDAY.next()); // MONDAY
System.out.println(Day.MONDAY.previous()); // SUNDAY
// Print only weekdays
for (Day d : Day.values()) {
if (d.isWeekday()) System.out.print(d + " ");
}
// MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY
}
}
Each constant can provide its own implementation of an abstract method declared in the enum body. This is the
constant-specific method body pattern — essentially a compile-time-verified strategy pattern
without subclasses.
public enum Operation {
PLUS("+") {
@Override
public double apply(double x, double y) { return x + y; }
},
MINUS("-") {
@Override
public double apply(double x, double y) { return x - y; }
},
TIMES("*") {
@Override
public double apply(double x, double y) { return x * y; }
},
DIVIDE("/") {
@Override
public double apply(double x, double y) {
if (y == 0) throw new ArithmeticException("Division by zero");
return x / y;
}
};
private final String symbol;
Operation(String symbol) { this.symbol = symbol; }
// Abstract — every constant must implement this
public abstract double apply(double x, double y);
@Override
public String toString() { return symbol; }
}
public class Main {
public static void main(String[] args) {
double x = 10, y = 3;
for (Operation op : Operation.values()) {
System.out.printf("%.1f %s %.1f = %.2f%n",
x, op, y, op.apply(x, y));
}
// 10.0 + 3.0 = 13.00
// 10.0 - 3.0 = 7.00
// 10.0 * 3.0 = 30.00
// 10.0 / 3.0 = 3.33
}
}
This pattern is more maintainable than a switch on this inside the method body: adding a
new constant forces you to implement apply, preventing accidental omissions.
Enums can also implement interfaces. Combining interface implementation with constant-specific method bodies
is a powerful way to attach polymorphic behaviour to a closed set of constants.
Enums are the ideal switch target. The compiler can verify exhaustiveness, and there is no need to
handle a default case when all constants are covered. Java 14+ switch expressions make this even
cleaner.
public enum Season { SPRING, SUMMER, AUTUMN, WINTER }
// Traditional switch statement
public static String describeOld(Season s) {
switch (s) {
case SPRING: return "Mild and blooming";
case SUMMER: return "Hot and sunny";
case AUTUMN: return "Cool and colourful";
case WINTER: return "Cold and snowy";
default: return "Unknown";
}
}
// Modern switch expression (Java 14+) — exhaustive, no fall-through
public static String describe(Season s) {
return switch (s) {
case SPRING -> "Mild and blooming";
case SUMMER -> "Hot and sunny";
case AUTUMN -> "Cool and colourful";
case WINTER -> "Cold and snowy";
// No default needed — all four constants are handled
};
}
// Switch with yield for multi-line arms
public static int monthsInSeason(Season s) {
return switch (s) {
case SPRING, SUMMER, AUTUMN, WINTER -> {
int months = 3;
System.out.println(s + " has " + months + " months");
yield months;
}
};
}
public static void main(String[] args) {
for (Season s : Season.values()) {
System.out.println(s + ": " + describe(s));
}
// SPRING: Mild and blooming
// SUMMER: Hot and sunny
// ...
}
java.util.EnumSet and java.util.EnumMap are specialised, high-performance implementations
of Set and Map for enum keys. They are backed by bit vectors and arrays respectively,
making them faster and more memory-efficient than their general-purpose counterparts.
import java.util.*;
public enum Permission {
READ, WRITE, DELETE, ADMIN
}
public class PermissionSystem {
// EnumSet — fast set operations using bit manipulation
public static void demonstrateEnumSet() {
EnumSet<Permission> userPerms = EnumSet.of(Permission.READ, Permission.WRITE);
EnumSet<Permission> adminPerms = EnumSet.allOf(Permission.class);
EnumSet<Permission> noPerms = EnumSet.noneOf(Permission.class);
EnumSet<Permission> readOnly = EnumSet.of(Permission.READ);
// Complement — everything except READ
EnumSet<Permission> notRead = EnumSet.complementOf(readOnly);
System.out.println("Not read: " + notRead); // [WRITE, DELETE, ADMIN]
// Range — ordinal order (READ=0, WRITE=1, DELETE=2)
EnumSet<Permission> basicRange = EnumSet.range(Permission.READ, Permission.DELETE);
System.out.println("Range: " + basicRange); // [READ, WRITE, DELETE]
// Test membership — O(1)
System.out.println(userPerms.contains(Permission.DELETE)); // false
System.out.println(adminPerms.containsAll(userPerms)); // true
}
// EnumMap — map from enum constants to values
public static void demonstrateEnumMap() {
EnumMap<Permission, String> descriptions = new EnumMap<>(Permission.class);
descriptions.put(Permission.READ, "Can view resources");
descriptions.put(Permission.WRITE, "Can create and update resources");
descriptions.put(Permission.DELETE, "Can remove resources");
descriptions.put(Permission.ADMIN, "Full system access");
// Iteration always follows declaration order
descriptions.forEach((perm, desc) ->
System.out.printf("%-10s : %s%n", perm, desc));
}
public static void main(String[] args) {
demonstrateEnumSet();
System.out.println("---");
demonstrateEnumMap();
}
}
The internal representation of EnumSet uses a single long for enums with 64 or fewer
constants (RegularEnumSet). Each bit corresponds to an ordinal. Set operations like
addAll, retainAll, and containsAll become single bitwise operations.
The compiler-generated values() method returns a fresh array of all constants in declaration order.
valueOf(String) performs a reverse lookup by name.
public enum Month {
JANUARY(31), FEBRUARY(28), MARCH(31), APRIL(30),
MAY(31), JUNE(30), JULY(31), AUGUST(31),
SEPTEMBER(30), OCTOBER(31), NOVEMBER(30), DECEMBER(31);
private final int days;
Month(int days) { this.days = days; }
public int days() { return days; }
public int days(int year) {
// Leap year only affects February
if (this == FEBRUARY) {
return isLeapYear(year) ? 29 : 28;
}
return days;
}
private static boolean isLeapYear(int year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
}
public class Main {
public static void main(String[] args) {
// Iterate all values
int totalDays = 0;
for (Month m : Month.values()) {
totalDays += m.days();
}
System.out.println("Days in a normal year: " + totalDays); // 365
// Use streams on values()
long longMonths = java.util.Arrays.stream(Month.values())
.filter(m -> m.days() == 31)
.count();
System.out.println("Months with 31 days: " + longMonths); // 7
// Lookup by name
Month m = Month.valueOf("MARCH");
System.out.println(m.days(2024)); // 31
// February 2024 (leap year)
System.out.println(Month.FEBRUARY.days(2024)); // 29
System.out.println(Month.FEBRUARY.days(2023)); // 28
// Build a lookup map (more efficient than calling valueOf() in a loop)
java.util.Map<Integer, Month> byOrdinal = new java.util.EnumMap<>(Month.class);
for (Month mo : Month.values()) {
byOrdinal.put(mo.ordinal() + 1, mo); // 1-based month number
}
System.out.println(byOrdinal.get(12)); // DECEMBER
}
}
values() allocates a new array on each call. For performance-sensitive loops inside tight hot spots,
cache the array in a local variable or a static constant rather than calling values() repeatedly.