00:00

Enum in Java – Type-Safe Constants with Behaviour

An enum (enumeration) is a special class that represents a fixed set of named constants, such as the days of the week, order statuses or planets. Declared with the enum keyword, Java enums are far more powerful than integer constants: they are type-safe, have names, can hold fields and methods, and work naturally with switch.

This tutorial covers the built-in enum methods, enums in switch, fields and constructors, constant-specific behaviour, interfaces, EnumSet and EnumMap, and the enum singleton.

1. Declaring and Using an Enum

EnumBasics.java
public class EnumBasics {
    enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }

    public static void main(String[] args) {
        Day today = Day.FRIDAY;
        System.out.println("today            = " + today);
        System.out.println("name()           = " + today.name());
        System.out.println("ordinal()        = " + today.ordinal());
        System.out.println("valueOf(\"SUNDAY\") = " + Day.valueOf("SUNDAY"));
        System.out.println("compareTo(MONDAY) = " + today.compareTo(Day.MONDAY));
        System.out.println("== comparison    = " + (today == Day.FRIDAY));

        System.out.print("values()         = ");
        for (Day d : Day.values()) System.out.print(d.name().substring(0, 3) + " ");
        System.out.println();

        try {
            Day.valueOf("FUNDAY");
        } catch (IllegalArgumentException e) {
            System.out.println("valueOf(\"FUNDAY\") -> IllegalArgumentException");
        }
    }
}
Output
today            = FRIDAY
name()           = FRIDAY
ordinal()        = 4
valueOf("SUNDAY") = SUNDAY
compareTo(MONDAY) = 4
== comparison    = true
values()         = MON TUE WED THU FRI SAT SUN 
valueOf("FUNDAY") -> IllegalArgumentException
MethodReturns
values()Array of all constants, in declaration order
valueOf(String)The constant with that exact name, or IllegalArgumentException
name()The constant's declared name
ordinal()Zero-based position in the declaration
compareTo()Comparison by ordinal (enums are Comparable)

Each constant is a single object, so enums can be compared safely with ==.

2. Why Enums Instead of int Constants?

The int constant problem
// int constants: no type safety
static final int PLACED = 0, SHIPPED = 1;
static final int RED = 0;
void setStatus(int status) { ... }
setStatus(RED);           // compiles! Any int is accepted, and it prints as "0"

// enums: checked by the compiler
enum Status { PLACED, SHIPPED }
enum Color { RED, GREEN }
void setStatus(Status status) { ... }
setStatus(Color.RED);     // compile error: incompatible types
  • Type safety: only valid constants of the right enum are accepted.
  • Readable output: toString() prints the name, not a number.
  • Namespace: constants are qualified by their type (Status.SHIPPED).
  • Behaviour: enums can have fields, methods and constructors.

3. Enums in switch

Switch expressions over enums are exhaustive: if you cover every constant, no default is needed, and the compiler reports an error when a new constant is added but not handled:

EnumSwitch.java
public class EnumSwitch {
    enum OrderStatus { PLACED, PACKED, SHIPPED, DELIVERED, CANCELLED }

    static String message(OrderStatus s) {
        return switch (s) {                       // exhaustive: no default needed
            case PLACED    -> "We have received your order";
            case PACKED    -> "Your order is packed";
            case SHIPPED   -> "Your order is on the way";
            case DELIVERED -> "Delivered. Enjoy!";
            case CANCELLED -> "Your order was cancelled";
        };
    }

    public static void main(String[] args) {
        for (OrderStatus s : OrderStatus.values())
            System.out.printf("%-9s : %s%n", s, message(s));
    }
}
Output
PLACED    : We have received your order
PACKED    : Your order is packed
SHIPPED   : Your order is on the way
DELIVERED : Delivered. Enjoy!
CANCELLED : Your order was cancelled

4. Fields, Constructors and Methods

Constants can carry data passed to a constructor, which is implicitly private:

EnumWithFields.java
public class EnumWithFields {
    enum Planet {
        MERCURY(3.303e+23, 2.4397e6),
        EARTH(5.976e+24, 6.37814e6),
        JUPITER(1.9e+27, 7.1492e7);

        private static final double G = 6.67300E-11;
        private final double mass;     // kg
        private final double radius;   // m

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

        double surfaceGravity() { return G * mass / (radius * radius); }
        double weightOf(double earthWeight) { return earthWeight / EARTH.surfaceGravity() * surfaceGravity(); }
    }

    public static void main(String[] args) {
        double kg = 70;
        for (Planet p : Planet.values())
            System.out.printf("A %.0f kg person weighs like %6.1f kg on %s%n", kg, p.weightOf(kg), p);
    }
}
Output
A 70 kg person weighs like   26.4 kg on MERCURY
A 70 kg person weighs like   70.0 kg on EARTH
A 70 kg person weighs like  177.1 kg on JUPITER

5. Constant-Specific Behaviour and Interfaces

Each constant can provide its own implementation of an abstract method, and an enum can implement interfaces:

EnumAbstractMethods.java
public class EnumAbstractMethods {
    // Constant-specific behaviour: each constant overrides an abstract method
    enum Operation {
        PLUS("+")  { int apply(int a, int b) { return a + b; } },
        MINUS("-") { int apply(int a, int b) { return a - b; } },
        TIMES("*") { int apply(int a, int b) { return a * b; } };

        private final String symbol;
        Operation(String symbol) { this.symbol = symbol; }
        abstract int apply(int a, int b);
        @Override public String toString() { return symbol; }
    }

    // Enums can implement interfaces too
    interface PriceRule { int finalPrice(int price); }

    enum Discount implements PriceRule {
        NONE, STUDENT, SENIOR;
        public int finalPrice(int price) {
            return switch (this) { case NONE -> price; case STUDENT -> price * 80 / 100; case SENIOR -> price / 2; };
        }
    }

    public static void main(String[] args) {
        for (Operation op : Operation.values())
            System.out.println("8 " + op + " 3 = " + op.apply(8, 3));
        for (Discount d : Discount.values())
            System.out.println(d + " pays " + d.finalPrice(500));
    }
}
Output
8 + 3 = 11
8 - 3 = 5
8 * 3 = 24
NONE pays 500
STUDENT pays 400
SENIOR pays 250

This replaces long if-else chains with polymorphism: adding a new Operation forces you to implement apply.

6. EnumSet and EnumMap

These specialised collections are backed by bit vectors and arrays, making them extremely fast and compact:

EnumCollections.java
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Map;

public class EnumCollections {
    enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }

    public static void main(String[] args) {
        EnumSet<Day> weekend = EnumSet.of(Day.SAT, Day.SUN);
        EnumSet<Day> weekdays = EnumSet.complementOf(weekend);
        EnumSet<Day> midweek = EnumSet.range(Day.TUE, Day.THU);
        System.out.println("weekend:  " + weekend);
        System.out.println("weekdays: " + weekdays);
        System.out.println("midweek:  " + midweek);

        Map<Day, Integer> hours = new EnumMap<>(Day.class);    // kept in declaration order
        hours.put(Day.WED, 8); hours.put(Day.MON, 9); hours.put(Day.FRI, 6);
        System.out.println("hours:    " + hours);
    }
}
Output
weekend:  [SAT, SUN]
weekdays: [MON, TUE, WED, THU, FRI]
midweek:  [TUE, WED, THU]
hours:    {MON=9, WED=8, FRI=6}

7. Enum Singleton

A single-constant enum is the simplest way to implement a singleton. The JVM guarantees one instance, thread-safe initialisation, and protection against reflection and serialization creating copies:

EnumSingleton.java
public class EnumSingleton {
    // The simplest thread-safe, serialization-safe singleton
    enum AppConfig {
        INSTANCE;
        private String env = "dev";
        String env() { return env; }
        void setEnv(String env) { this.env = env; }
    }

    public static void main(String[] args) {
        AppConfig.INSTANCE.setEnv("production");
        System.out.println("env = " + AppConfig.INSTANCE.env());
        System.out.println("same instance: " + (AppConfig.INSTANCE == AppConfig.valueOf("INSTANCE")));
    }
}
Output
env = production
same instance: true

8. Rules and Facts

  • Every enum implicitly extends java.lang.Enum, so it cannot extend another class; it can implement interfaces.
  • Enums are implicitly final (unless constants have bodies) and cannot be instantiated with new.
  • Constants must be listed first; fields and methods follow after a semicolon.
  • Constructors are always private (or package-private), never public.
  • Enums are Serializable and Comparable; serialization stores only the name.
  • Do not persist ordinal() values (for example in databases): reordering constants changes them. Store the name instead.

9. Summary

  • An enum defines a fixed set of type-safe constants.
  • Use values(), valueOf(), name() and ordinal(); compare with ==.
  • Enums can have fields, constructors, methods, constant-specific bodies and implement interfaces.
  • Use them in exhaustive switch expressions, and with EnumSet and EnumMap.
  • A single-element enum is the best singleton.

Related Tutorials