Topic 109 of 119 14. Additional Java Topics Interactive Tutorial

Java Enums: Type-Safe Enumerations, Fields, Methods, and Interfaces

9 mins read Last updated: 22 Aug 2026 15,857 views

Direct Answer & Core Concept

Java enum is a special type used to represent a fixed set of named constants. Instead of using arbitrary integers or strings for values such as days, statuses, directions, or user roles, an enum gives those values a clear and type-safe representation.

Java enums can do much more than simply store constants. An enum can have fields, constructors, methods, interfaces, and even constant-specific behavior. Because an enum is a real Java type, it can also be used with collections, switch statements, and generic APIs.

In this tutorial, you will learn how to create enums, access enum constants, use fields and methods, define constructors, implement interfaces, compare enum values, use enums with switch, and work with EnumSet and EnumMap.

Detailed Explanation & Mechanics

What Is an Enum in Java?

An enum, short for enumeration, defines a restricted set of predefined values.

For example:

Main.java
Run Online
enum Direction {
    NORTH, SOUTH, EAST, WEST
}

The Direction type can contain only the constants defined by the enum.

You can create a variable like this:

Main.java
Run Online
Direction direction = Direction.NORTH;

This is safer than representing directions with arbitrary strings:

Main.java
Run Online
String direction = "north";

With a String, values such as "North", "NORTH", "north", or "Nroth" can accidentally appear. An enum restricts the value to the defined constants.


Why Use Enums?

Enums are useful when a variable should have one value from a known, fixed set.

Common examples include:

  • Days of the week

  • Months

  • Order status

  • Payment status

  • User roles

  • Directions

  • Access levels

  • HTTP-like application states

  • Priority levels

  • Traffic-light states

For example:

Main.java
Run Online
enum OrderStatus {
    PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED
}

Now an order can use:

Main.java
Run Online
OrderStatus status = OrderStatus.SHIPPED;

This makes the possible states explicit in the type itself.


Basic Enum Syntax

The simplest enum declaration looks like this:

Main.java
Run Online
enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

You can then use:

Main.java
Run Online
Day today = Day.MONDAY;
System.out.println(today);

Output:

Each constant is an instance of the enum type.


Enum Constants

The values inside an enum are called enum constants.

For:

Main.java
Run Online
enum Size {
    SMALL, MEDIUM, LARGE
}

the constants are:

Main.java
Run Online
Size.SMALLSize.MEDIUMSize.LARGE

They are not ordinary string values.

For example:

The variable size has type Size.


Enum Values Are Type-Safe

Consider:

Main.java
Run Online
enum Priority {
    LOW, MEDIUM, HIGH
}

This is valid:

Main.java
Run Online
Priority priority = Priority.HIGH;

But this is not:

Main.java
Run Online
Priority priority = "HIGH";

The string "HIGH" and the enum constant Priority.HIGH are different types.

This compile-time type checking is one of the major benefits of enums.


Enum With a switch Statement

Enums work naturally with switch.

Main.java
Run Online
enum Day {
    MONDAY, TUESDAY, WEDNESDAY
}public class Main {
    public static void main(String[] args) {
        Day day = Day.MONDAY;
        switch (day) {
            case MONDAY: System.out.println("Start of the week");
            break;
            case TUESDAY: System.out.println("Tuesday");
            break;
            case WEDNESDAY: System.out.println("Wednesday");
            break;
        }
    }
}

Inside the switch, you normally write the enum constant directly rather than repeating the enum type.


Modern Switch Expressions With Enums

Modern Java also supports switch expressions.

Main.java
Run Online
enum Day {
    MONDAY, TUESDAY, SATURDAY, SUNDAY
}public class Main {
    public static void main(String[] args) {
        Day day = Day.SATURDAY;
        String type = switch (day) {
            case SATURDAY, SUNDAY -> "Weekend";
            default -> "Weekday";
        };
        System.out.println(type);
    }
}

Output:

Switch expressions are often more concise when the goal is to produce a value.


The values() Method

Every enum provides a values() method that returns its constants.

Example:

Main.java
Run Online
enum Color {
    RED, GREEN, BLUE
}public class Main {
    public static void main(String[] args) {
        for (Color color : Color.values()) {
            System.out.println(color);
        }
    }
}

Output:

This is useful when you need to iterate over all available enum constants.


The valueOf() Method

Enums also provide a valueOf() method for converting a matching string into an enum constant.

Main.java
Run Online
enum Status {
    PENDING, APPROVED, REJECTED
}

Then:

Main.java
Run Online
Status status = Status.valueOf("APPROVED");
System.out.println(status);

Output:

The string must match the enum constant name exactly.

For example:

Main.java
Run Online
Status.valueOf("approved");

does not automatically perform case conversion.

It throws:

when no matching constant exists.


Using name() With Enums

Every enum constant has a name() method.

Main.java
Run Online
enum Status {
    PENDING, APPROVED
}Status status = Status.APPROVED;
System.out.println(status.name());

Output:

name() returns the exact declared enum constant name.


toString() and Enum Display

An enum also inherits behavior from Enum, including toString().

Normally:

Main.java
Run Online
System.out.println(Status.APPROVED);

produces:

But toString() can be overridden when a different display representation is required.

For example:

Main.java
Run Online
enum Status {
    PENDING, APPROVED;
    @Override public String toString() {
        return name().toLowerCase();
    }
}

Now:

Main.java
Run Online
System.out.println(Status.APPROVED);

can produce:

Use this carefully when the enum's display form is part of an application's API or data format.


Enum Fields

An enum can contain fields just like a normal class.

For example:

Main.java
Run Online
enum Planet {
    EARTH(5.97), MARS(0.642), JUPITER(1898.0);
    private final double mass;
    Planet(double mass) {
        this.mass = mass;
    } public double getMass() {
        return mass;
    }
}

Now:

Main.java
Run Online
System.out.println(Planet.EARTH.getMass());

Output:

This makes enums useful for representing constants that have associated data.


Enum Constructors

Enums can have constructors.

Example:

Main.java
Run Online
enum Level {
    LOW(1), MEDIUM(2), HIGH(3);
    private final int code;
    Level(int code) {
        this.code = code;
    } public int getCode() {
        return code;
    }
}

Each constant passes its value to the enum constructor:

Main.java
Run Online
LOW → 1MEDIUM → 2HIGH → 3

Enum constructors are not called directly with new.

You cannot write:

Enum instances are controlled by the enum type itself.


Enum Constructors Are Effectively Private

Enum constructors cannot be invoked from outside the enum.

For example:

Main.java
Run Online
enum Status {
    ACTIVE, INACTIVE;
    Status() {
        System.out.println("Constructor called");
    }
}

The enum system creates the constants.

You use:

rather than constructing a new Status.


Enum Methods

Enums can contain methods.

Main.java
Run Online
enum TrafficLight {
    RED, YELLOW, GREEN;
    public boolean allowsDriving() {
        return this == GREEN;
    }
}

Usage:

Main.java
Run Online
TrafficLight light = TrafficLight.GREEN;
System.out.println( light.allowsDriving());

Output:

This allows behavior to be associated directly with the enum type.


Enum With Fields and Methods

Here is a complete example:

Main.java
Run Online
enum OrderStatus {
    PENDING("Order received"), SHIPPED("Order shipped"), DELIVERED("Order delivered"), CANCELLED("Order cancelled");
    private final String message;
    OrderStatus(String message) {
        this.message = message;
    } public String getMessage() {
        return message;
    }
}

Usage:

Main.java
Run Online
OrderStatus status = OrderStatus.SHIPPED;
System.out.println(status.getMessage());

Output:

This is a common pattern for giving enum constants additional application-specific information.


Enum Implementing an Interface

An enum can implement one or more interfaces.

Example:

Main.java
Run Online
interface Describable {
    String getDescription();
}enum Priority implements Describable {
    LOW, MEDIUM, HIGH;
    @Override public String getDescription() {
        return "Priority: " + name();
    }
}

Usage:

Main.java
Run Online
Priority priority = Priority.HIGH;
System.out.println( priority.getDescription());

Output:

This is useful when enum constants need to follow a common contract.


Enum With an Interface and Fields

A more practical example:

Main.java
Run Online
interface CodeProvider {
    int getCode();
}enum PaymentStatus implements CodeProvider {
    PENDING(100), SUCCESS(200), FAILED(500);
    private final int code;
    PaymentStatus(int code) {
        this.code = code;
    } @Override public int getCode() {
        return code;
    }
}

Usage:

Main.java
Run Online
PaymentStatus status = PaymentStatus.SUCCESS;
System.out.println(status.getCode());

Output:

The enum now combines:

  • Fixed constants

  • Associated data

  • A method

  • An interface contract


Constant-Specific Enum Behavior

Each enum constant can have its own implementation of an abstract method.

Example:

Main.java
Run Online
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);
}

Usage:

Main.java
Run Online
System.out.println( Operation.ADD.apply(10, 5));
System.out.println( Operation.MULTIPLY.apply(10, 5));

Output:

This technique allows each constant to provide different behavior while keeping everything inside one enum type.


Comparing Enum Values

The recommended way to compare enum constants is normally ==.

Example:

Main.java
Run Online
enum Status {
    ACTIVE, INACTIVE
}Status status = Status.ACTIVE;
if (status == Status.ACTIVE) {
    System.out.println("Active");
}

Enum constants are unique instances within their enum type, making identity comparison appropriate.

You can also use:

Main.java
Run Online
status.equals(Status.ACTIVE)

but == is simple and avoids concerns about a null receiver.

For example:

Main.java
Run Online
if (status == Status.ACTIVE) {
    // ...
}

is safe even when status is null; it simply evaluates to false.


Enum and ordinal()

Every enum constant has an ordinal position.

Example:

Main.java
Run Online
enum Size {
    SMALL, MEDIUM, LARGE
}

Then:

Main.java
Run Online
System.out.println(Size.SMALL.ordinal());
System.out.println(Size.MEDIUM.ordinal());
System.out.println(Size.LARGE.ordinal());

Output:

The first constant has ordinal 0.


Should You Store ordinal() in a Database?

Generally, no.

Do not use:

as a persistent business identifier unless you deliberately control the consequences.

If the enum changes:

Main.java
Run Online
enum Size {
    SMALL, MEDIUM, LARGE
}

and later becomes:

Main.java
Run Online
enum Size {
    EXTRA_SMALL, SMALL, MEDIUM, LARGE
}

the ordinal values shift.

Instead, use an explicit field:

Main.java
Run Online
enum Size {
    SMALL(10), MEDIUM(20), LARGE(30);
    private final int code;
    Size(int code) {
        this.code = code;
    } public int getCode() {
        return code;
    }
}

Now the application controls the business codes explicitly.


Enum With Custom Codes

This is a common real-world pattern:

Main.java
Run Online
enum Country {
    INDIA("IN"), UNITED_STATES("US"), UNITED_KINGDOM("GB");
    private final String code;
    Country(String code) {
        this.code = code;
    } public String getCode() {
        return code;
    }
}

Usage:

Main.java
Run Online
System.out.println( Country.INDIA.getCode());

Output:

This is preferable to relying on ordinal positions when the value needs to remain stable.


Enum With a Lookup Method

You can create a method that finds an enum constant from an external code.

Main.java
Run Online
enum Country {
    INDIA("IN"), UNITED_STATES("US"), UNITED_KINGDOM("GB");
    private final String code;
    Country(String code) {
        this.code = code;
    } public String getCode() {
        return code;
    } public static Country fromCode(String code) {
        for (Country country : values()) {
            if (country.code.equals(code)) {
                return country;
            }
        } throw new IllegalArgumentException( "Unknown country code: " + code);
    }
}

Usage:

Main.java
Run Online
Country country = Country.fromCode("IN");
System.out.println(country);

Output:

For a small enum, this simple loop is often perfectly adequate.


Enum and Null Values

An enum variable can contain null because it is a reference type.

For example:

But calling:

causes:

Always consider whether an enum variable can be null.

If a value should always exist, enforce that rule through your application's design.


Enum as a Method Parameter

Enums work well as method parameters because they restrict callers to valid values.

Main.java
Run Online
enum PaymentMethod {
    CASH, CARD, UPI
}public static void pay( PaymentMethod method) {
    System.out.println( "Payment method: " + method);
}

Call:

This is clearer than:

because the compiler knows which values are allowed.


Enum as a Method Return Type

An enum can also be returned from methods.

Main.java
Run Online
enum UserStatus {
    ACTIVE, BLOCKED, DELETED
}public static UserStatus getStatus() {
    return UserStatus.ACTIVE;
}

This gives callers a well-defined set of possible results.


Enum With EnumSet

Java provides a specialized collection called EnumSet for enum values.

Example:

Main.java
Run Online
import java.util.EnumSet;
enum Permission {
    READ, WRITE, DELETE, EXECUTE
}

Create a set:

Main.java
Run Online
EnumSet<Permission> permissions = EnumSet.of( Permission.READ, Permission.WRITE);

Then:

Main.java
Run Online
System.out.println(permissions);

EnumSet is designed specifically for enum elements and is often more efficient and expressive than using a general-purpose Set for the same purpose.


Useful EnumSet Operations

Main.java
Run Online
EnumSet<Permission> all = EnumSet.allOf(Permission.class);

This creates a set containing all enum constants.

You can also create an empty set:

Main.java
Run Online
EnumSet<Permission> none = EnumSet.noneOf(Permission.class);

And create a range for enums that have a natural declaration order:

Main.java
Run Online
EnumSet<Permission> range = EnumSet.range( Permission.READ, Permission.DELETE);

Use ranges carefully because they follow enum declaration order, not a separate business ordering.


Enum With EnumMap

EnumMap is a specialized map designed for enum keys.

Example:

Main.java
Run Online
import java.util.EnumMap;
enum Department {
    HR, IT, SALES
}EnumMap<Department, String> managers = new EnumMap<>(Department.class);
managers.put(Department.HR, "Amit");
managers.put(Department.IT, "Rahul");
managers.put(Department.SALES, "Priya");

Retrieve a value:

Main.java
Run Online
System.out.println( managers.get(Department.IT));

Output:

When the key type is an enum, EnumMap clearly expresses that relationship.


Enum and switch Best Practices

For a small number of enum states, a switch can be very readable:

Main.java
Run Online
switch (status) {
    case PENDING: // ...
    break;
    case APPROVED: // ...
    break;
    case REJECTED: // ...
    break;
}

For behavior that naturally belongs to each enum constant, however, an enum method can sometimes produce a cleaner design:

Main.java
Run Online
enum Status {
    PENDING {
        public String message() {
            return "Waiting";
        }
    }, APPROVED {
        public String message() {
            return "Approved";
        }
    }, REJECTED {
        public String message() {
            return "Rejected";
        }
    };
    public abstract String message();
}

Choose the approach based on where the behavior logically belongs.


Enum and Serialization

Enums have special serialization behavior in Java.

An enum constant is serialized by its name rather than by serializing an ordinary object field-by-field in the same way as a typical Serializable class.

This is one reason enum constants are useful for stable symbolic values.

You generally do not implement Serializable yourself for an enum.


Enum Cannot Extend Another Class

An enum cannot extend an arbitrary class.

For example, this is not allowed:

Main.java
Run Online
enum Status extends SomeClass {
}

An enum already derives from:

However, an enum can implement interfaces:

Main.java
Run Online
interface Describable {
    String describe();
}enum Status implements Describable {
    ACTIVE, INACTIVE;
    public String describe() {
        return name();
    }
}

This gives enums a way to share behavior contracts without extending another class.


Enum Is a Class-Like Type

An enum may look simple:

Main.java
Run Online
enum Color {
    RED, GREEN, BLUE
}

But enum types can contain:

  • Fields

  • Constructors

  • Methods

  • Interfaces

  • Abstract methods

  • Constant-specific implementations

  • Annotations

This makes enums much more powerful than a simple list of strings.


Built-In Methods From java.lang.Enum

Every enum implicitly extends java.lang.Enum.

Useful methods include:

Main.java
Run Online
name()ordinal()toString()compareTo()equals()hashCode()getDeclaringClass()

For example:

Main.java
Run Online
Status status = Status.ACTIVE;
System.out.println(status.name());
System.out.println(status.ordinal());
System.out.println(status.getDeclaringClass());

The exact method behavior should be understood before using enum values as business identifiers.


compareTo() With Enums

Enum constants can be compared using their natural declaration order.

Example:

Main.java
Run Online
enum Priority {
    LOW, MEDIUM, HIGH
}

Then:

Main.java
Run Online
System.out.println( Priority.LOW.compareTo(Priority.HIGH));

The result is negative because LOW appears before HIGH.

However, declaration order is not automatically the same thing as business priority.

If business ordering is important, an explicit field or comparator can make that intention clearer.


Common Enum Mistakes

Mistake 1: Using Strings for Fixed Values

Instead of:

Main.java
Run Online
String status = "approved";

consider:

Main.java
Run Online
Status status = Status.APPROVED;

when the set of possible values is fixed.


Mistake 2: Using ordinal() as a Business ID

Avoid relying on:

Main.java
Run Online
Status.APPROVED.ordinal()

for database identifiers or external APIs.

Use an explicit field instead.


Mistake 3: Assuming valueOf() Is Case-Insensitive

This:

Main.java
Run Online
Status.valueOf("approved");

does not automatically match:

If input comes from users or external systems, normalize and validate it explicitly.


Mistake 4: Forgetting Unknown External Values

External systems may introduce values that your current enum does not contain.

For example:

can throw IllegalArgumentException.

Handle external input appropriately.


Mistake 5: Putting Too Much Logic Into an Enum

Enums can contain methods and complex behavior, but that does not mean every piece of business logic belongs inside the enum.

Keep the design focused and readable.


Enum vs Constants

Before enums, developers often used constants:

Main.java
Run Online
public static final int LOW = 1;
public static final int MEDIUM = 2;
public static final int HIGH = 3;

This works, but the values are just integers.

A method could accidentally receive:

With an enum:

Main.java
Run Online
enum Priority {
    LOW, MEDIUM, HIGH
}

the method can require:

Main.java
Run Online
void process(Priority priority)

The compiler then helps enforce the intended type.


Enum vs String Constants

Another common approach is:

Main.java
Run Online
public static final String ACTIVE = "active";
public static final String INACTIVE = "inactive";

This is still weaker than:

Main.java
Run Online
enum Status {
    ACTIVE, INACTIVE
}

because strings can contain arbitrary values and do not communicate the complete allowed set through the type system.

Enums are usually clearer when the values form a closed set.


Practical Example: Order Processing

Consider an online store:

Main.java
Run Online
enum OrderStatus {
    CREATED, CONFIRMED, PACKED, SHIPPED, DELIVERED, CANCELLED
}

A method can then accept:

Main.java
Run Online
public void updateStatus(OrderStatus status) {
    // update order
}

Usage:

Main.java
Run Online
updateStatus(OrderStatus.SHIPPED);

This makes the order state model explicit.

You can also add behavior:

Main.java
Run Online
enum OrderStatus {
    CREATED("Order created"), CONFIRMED("Order confirmed"), PACKED("Order packed"), SHIPPED("Order shipped"), DELIVERED("Order delivered"), CANCELLED("Order cancelled");
    private final String message;
    OrderStatus(String message) {
        this.message = message;
    } public String getMessage() {
        return message;
    }
}

Now each state contains its own display message.


Practical Example: User Roles

Main.java
Run Online
enum Role {
    ADMIN, MANAGER, EDITOR, USER
}

Use it in a class:

Main.java
Run Online
class User {
    private String name;
    private Role role;
    public User(String name, Role role) {
        this.name = name;
        this.role = role;
    } public boolean canEdit() {
        return role == Role.ADMIN || role == Role.MANAGER || role == Role.EDITOR;
    }
}

The enum makes the allowed role values explicit.


Practical Example: Priority With a Numeric Level

Main.java
Run Online
enum Priority {
    LOW(1), MEDIUM(2), HIGH(3), CRITICAL(4);
    private final int level;
    Priority(int level) {
        this.level = level;
    } public int getLevel() {
        return level;
    }
}

Usage:

Main.java
Run Online
Priority priority = Priority.CRITICAL;
if (priority.getLevel() >= 3) {
    System.out.println("Handle urgently");
}

Here, the explicit level field represents business meaning instead of relying on ordinal().


Java Enum Cheat Sheet

Main.java
Run Online
enum↓Defines a fixed set of constantsvalues()↓Returns all enum constantsvalueOf()↓Converts an exact matching name to an enum constantname()↓Returns the declared constant nameordinal()↓Returns declaration positionEnumSet↓Efficient set for enum valuesEnumMap↓Map optimized for enum keys

Basic enum

Main.java
Run Online
enum Status {
    ACTIVE, INACTIVE
}

Enum with field

Main.java
Run Online
enum Status {
    ACTIVE(1), INACTIVE(0);
    private final int code;
    Status(int code) {
        this.code = code;
    } public int getCode() {
        return code;
    }
}

Enum with interface

Main.java
Run Online
interface Describable {
    String describe();
}enum Status implements Describable {
    ACTIVE, INACTIVE;
    @Override public String describe() {
        return name();
    }
}

Frequently Asked Questions

What is an enum in Java?

An enum is a special Java type used to represent a fixed set of named constants.

Are enum constants objects?

Yes. Each enum constant is an instance of its enum type.

Can an enum have constructors?

Yes. Enum constructors can initialize fields associated with each constant, but callers cannot instantiate enum objects with new.

Can an enum have methods?

Yes. An enum can contain instance methods, static methods, abstract methods, and constant-specific implementations.

Can an enum implement an interface?

Yes. An enum can implement one or more interfaces.

Can an enum extend a class?

An enum cannot extend an arbitrary class because enum types already extend java.lang.Enum.

What does values() do?

It returns an array containing the enum constants in their declaration order.

What does valueOf() do?

It returns the enum constant whose name exactly matches the supplied string, or throws IllegalArgumentException if there is no match.

What is ordinal()?

ordinal() returns the zero-based declaration position of an enum constant.

Should ordinal() be used as a database ID?

Generally, no. Enum declaration order can change. Use an explicit stable code when a persistent identifier is required.

What is EnumSet?

EnumSet is a specialized Set implementation designed specifically for enum elements.

What is EnumMap?

EnumMap is a specialized Map implementation designed for enum keys.

Suggested Graphics and Page Design

Graphic 1 — Basic Enum Structure

Architecture Diagram
             Java Enum
                 │
       ┌─────────┼─────────┐
       ▼         ▼         ▼
    Constant   Constant   Constant
      LOW      MEDIUM       HIGH

Graphic 2 — Enum With Data

Architecture Diagram
Priority Enum
      │
 ┌────┼─────────────┐
 ▼    ▼             ▼
LOW  MEDIUM        HIGH
 1      2            3

Graphic 3 — Enum With Interface

Architecture Diagram
       Interface
      Describable
           │
           ▼
      Java Enum
           │
   ┌───────┼───────┐
   ▼       ▼       ▼
 ACTIVE  PENDING  CLOSED

Graphic 4 — Enum Collections

Architecture Diagram
Enum Values
     │
 ┌───┴────┐
 ▼        ▼
EnumSet  EnumMap
  │        │
 Set      Key → Value

Use clean technical diagrams with Java code snippets, simple boxes, and arrows. Avoid generic stock photos because diagrams communicate the enum concepts more effectively.

Runnable Code Example: Java Enums

Java 17+
Main.java
Run Online
public class EnumDemo {
    public enum OrderStatus {
        PENDING(1, "Awaiting Payment"), PROCESSING(2, "Preparing for Dispatch"), SHIPPED(3, "In Transit"), DELIVERED(4, "Delivered to Customer");
        private final int code;
        private final String description;
        OrderStatus(int code, String description) {
            this.code = code;
            this.description = description;
        } public String getDescription() {
            return description;
        }
    } public static void main(String[] args) {
        OrderStatus status = OrderStatus.SHIPPED;
        System.out.println("Status: " + status.name() + " -> " + status.getDescription());
    }
}

Common Java Technical Interview Questions & Answers

Interview Prep
Q1

Can a Java enum extend another class?

No, all Java enums implicitly extend java.lang.Enum, but they can implement multiple interfaces.
Q2

Why is Enum the best way to implement a Singleton in Java?

Because the JVM guarantees that only one instance of each enum constant is ever instantiated, even in the presence of complex serialization and reflection attacks.

Key Takeaways, Best Practices & Clean Code Rules

Java Best Practice & Production Rules

Use Enums instead of static final int/String constants for fixed sets of business options (order status, payment types, roles).