The order that was never shipped
Kabir's shop app stores each order's status as a String. The warehouse code sets "Shipped", the delivery code checks for "SHIPPED", and a new teammate types "Shiped". Customers see “Processing” for days.
Sir, three people spelled “shipped” three ways, and Java was fine with all of them!
Because to Java they're just text. Any String is allowed. What you want is a type that has exactly four possible values. That's an enum: enum Status { PLACED, PACKED, SHIPPED, DELIVERED }.
And if someone types Status.SHIPED?
Compile error: cannot find symbol. And there's more: each value is a real object. It can carry data, like a delivery-time estimate, and methods, like “what's the next status?”. Let's build it.
What is an enum?
An enum (enumeration type), declared with the keyword enum, is a special class that defines a fixed set of named constants. Each constant is a public static final instance of the enum, created once when the enum class is initialised. Every enum implicitly extends java.lang.Enum, so it cannot extend another class, but it may implement interfaces and declare fields, constructors (always private) and methods. Enum objects cannot be created with new. Introduced in Java 5.
An enum is a list of the only allowed values for something: days of the week, order statuses, coach classes. The compiler stops anything else, and each value is an object you can give data and methods to.
Words you'll meet in this lesson
Status.SHIPPED. Written in CAPITALS by convention."SHIPPED".IllegalArgumentException.| Implicit parent | java.lang.Enum<E>, so an enum can't extends anything else |
|---|---|
| Constructor | Always private (writing public is a compile error); runs once per constant |
| Creating objects | Not allowed: new Status() is a compile error |
| Comparing | == is safe and preferred: there is exactly one object per constant |
| Free methods | name(), ordinal(), toString(), compareTo(), values(), valueOf() |
Exam tip: an enum is implicitly final (unless a constant has its own body) and extends java.lang.Enum; its constructor is implicitly private; the constants must be the first thing in the enum body, followed by a semicolon if anything else comes after them. Use == to compare enum values.
Four ways to use an enum
1. Plain list
Just names: enum Day { MON, TUE, … }. Comes with values(), ordinal(), name().
2. In a switch
Branch on the value; inside case you write just SHIPPED.
3. With fields and methods
Each constant carries data: AC3(2.5), plus methods like fare(base).
4. EnumMap / EnumSet
Fast, ordered collections keyed by enum constants.
collections1. A plain enum and its built-in methods
import java.util.Arrays;
enum Status { PLACED, PACKED, SHIPPED, DELIVERED }
public class Main {
public static void main(String[] args) {
Status s = Status.SHIPPED;
System.out.println(s); // toString() = name
System.out.println(s.ordinal()); // position, from 0
System.out.println(Arrays.toString(Status.values()));
System.out.println(Status.valueOf("PACKED"));
System.out.println(s == Status.SHIPPED);
System.out.println(Status.PACKED.compareTo(Status.DELIVERED)); // by ordinal
}
}valueOf is exact: Status.valueOf("Shipped") or "SHIPED" throws IllegalArgumentException: No enum constant Status.SHIPED. When reading user input, normalise it (trim().toUpperCase()) and catch the exception.
2. Switch on an enum
enum Status { PLACED, PACKED, SHIPPED, DELIVERED }
public class Main {
static String message(Status s) {
return switch (s) { // switch expression (Java 14+)
case PLACED, PACKED -> "We're preparing your order";
case SHIPPED -> "On the way";
case DELIVERED -> "Delivered. Enjoy!";
};
}
public static void main(String[] args) {
for (Status s : Status.values()) {
System.out.println(s + ": " + message(s));
}
}
}Inside a case you write SHIPPED, not Status.SHIPPED. A switch expression over an enum must cover every constant (or have default), so if someone adds RETURNED later, javac points at every switch that forgot it.
3. Fields, a constructor and methods
enum Coach {
SL(1.0), AC3(2.5), AC2(3.6); // each constant calls the constructor
private final double factor;
Coach(double factor) { // private by default
this.factor = factor;
}
int fare(int base) {
return (int) Math.round(base * factor);
}
}
public class Main {
public static void main(String[] args) {
for (Coach c : Coach.values()) {
System.out.println(c + " fare for ₹400 base: ₹" + c.fare(400));
}
}
}Constants can even override a method each in their own way (constant-specific bodies):
enum Op {
ADD { int apply(int a, int b) { return a + b; } },
MUL { int apply(int a, int b) { return a * b; } };
abstract int apply(int a, int b);
}
public class Main {
public static void main(String[] args) {
System.out.println(Op.ADD.apply(3, 4) + " " + Op.MUL.apply(3, 4));
}
}4. EnumMap and EnumSet
import java.util.EnumMap;
import java.util.EnumSet;
enum Status { PLACED, PACKED, SHIPPED, DELIVERED }
public class Main {
public static void main(String[] args) {
EnumMap<Status, Integer> count = new EnumMap<>(Status.class);
count.put(Status.DELIVERED, 5);
count.put(Status.PLACED, 2);
System.out.println(count); // always in declaration order
EnumSet<Status> active = EnumSet.range(Status.PACKED, Status.SHIPPED);
System.out.println(active.contains(Status.SHIPPED));
}
}Rule of thumb: if a variable can only hold one of a small, fixed set of values known while coding (statuses, roles, sizes, payment modes), make it an enum. Don't use ordinal() for business data or database storage: inserting a new constant in the middle shifts every number. Store name() instead, or a code field of your own.
What javac builds from an enum
You write three words
javac compiles this into an ordinary class file,public enum Size { SMALL, MEDIUM, LARGE }Size.class.It becomes a final class extending Enum
Each constant is apublic final class Size extends java.lang.Enum<Size> { public static final Size SMALL; public static final Size MEDIUM; public static final Size LARGE; private static final Size[] $VALUES; public static Size[] values(); public static Size valueOf(java.lang.String); private Size(); private static Size[] $values(); static {}; }public static finalfield.values()andvalueOf()are generated for you. The constructor is private.The static initialiser creates the objects
On first use ofSize, the JVM runsstatic {}: it createsnew Size("SMALL", 0),new Size("MEDIUM", 1),new Size("LARGE", 2)(javac passes the name and ordinal toEnum's constructor), stores them in the fields, and fills the$VALUESarray.Every use refers to the same three objects
Size.LARGEanywhere in the program reads the same static field, so it's the same object. That's why==is correct for enums, and why enums are a simple way to make a singleton.values() hands out a copy
values()returns$VALUES.clone(): a new array each time, so callers can't change the real list. In a hot loop, store it once in a variable.
From enum declaration to objects: both views
Left: the code. Right: the enum objects the JVM creates and the static fields pointing to them.
- The enum is declared with two constants, each passing a fare factor. Nothing runs yet.
Coach.AC3is read for the first time.Coach.AC3is read again into another variable.c == dprints true.c.fare(400)uses AC3's own factor: 400 × 2.5 = 1000.values()returns all constants in declaration order.
- Declaring an enum only defines a class. No Coach objects exist until the class is first used.
- First use triggers the static initialiser:
new Coach("SL", 0, 1.0)andnew Coach("AC3", 1, 2.5)are created once and stored in static fields.cgets a reference to AC3. - Reading
Coach.AC3again returns the same object. Nothing new is created; enum constructors never run again. canddhold the same reference, so==is true. That's why==is the right way to compare enums.fareruns withthis= the AC3 object and uses itsfactorfield, 2.5.values()returns a fresh copy of the internal array, so no caller can change the real list of constants.
Enums in real systems
Wherever a value must be one of a known list, production code reaches for an enum.
Watch it with an example: a delivery status page
A food-delivery order moves through an enum's states. Watch the app call next() and refuse a skip from PLACED straight to DELIVERED.
- A new order starts as
Status.PLACED. Its display text comes from the constant'slabelfield. - When the restaurant accepts, the server calls
status.next(), which returns the following constant: COOKING. - The rider's app moves it to PICKED_UP, again with
next(). - Finally DELIVERED. Every state and its text live in one enum.
- If a buggy request tries to set DELIVERED while the order is still PLACED, a method like
canMoveToon the enum refuses it. (Simplified: real apps also record who changed the status and when.)
1. Order and ticket statuses
E-commerce orders, support tickets and loan applications move through fixed states. An enum lists them, and often holds the allowed next states, so an order can't jump from PLACED to DELIVERED.
2. Payment modes with their own rules
A PaymentMode enum (UPI, CARD, NETBANKING, COD) can carry a fee percentage and a limit per mode, so the rules live with the value instead of in scattered if-else chains.
3. Roles and permissions
A school portal's Role enum (ADMIN, TEACHER, STUDENT) with an EnumSet<Permission> per role makes access checks fast and readable.
4. Libraries and the JDK
The JDK itself uses enums: DayOfWeek, Month, TimeUnit, RoundingMode, Thread.State. Web frameworks use them for HTTP methods and status codes.
Examples are simplified. When enums are saved to a database, teams store the name (or a stable code), never the ordinal.
An order tracker that can't skip a step
enum Status {
PLACED("Order received", 0),
PACKED("Packed at warehouse", 1),
SHIPPED("Out for delivery", 2),
DELIVERED("Delivered", 0);
private final String label;
private final int daysLeft;
Status(String label, int daysLeft) {
this.label = label;
this.daysLeft = daysLeft;
}
Status next() {
if (this == DELIVERED) {
return DELIVERED; // last step: stay
}
return values()[ordinal() + 1]; // the following constant
}
String label() { return label; }
int daysLeft() { return daysLeft; }
}
public class Main {
public static void main(String[] args) {
Status s = Status.PLACED;
while (s != Status.DELIVERED) {
System.out.println(s + " · " + s.label() + " · " + s.daysLeft() + " day(s)");
s = s.next();
}
System.out.println(s + " · " + s.label());
}
}PLACED("Order received", 0)Each constant passes its own data to the constructor, once, when the enum is initialised.this == DELIVEREDComparing enums with==is safe: one object per constant.values()[ordinal() + 1]Uses the declaration order to find the next status. Fine inside the enum; don't store ordinals elsewhere.while (s != Status.DELIVERED)Outside the enum, constants need the type name:Status.DELIVERED.
One object per constant
This trace is recorded from the real JVM. On the first line that uses Coach, the JVM initialises the enum class and creates every constant; the trace shows them already built. Look inside a constant on the heap: besides factor it carries name and ordinal, inherited from java.lang.Enum.
Worked examples: real problems, solved and traced in memory
Three business problems where an enum carries the rules. Watch method calls run on the constant objects.
Total fare for a family booking
Problem statement
A family books 2 tickets in AC3 and 1 in SL on a route with a base fare of ₹400. Each coach class multiplies the base fare. Compute the total.
AC3 × 2, SL × 1, base ₹400
Total: ₹2400
Approach
- An enum
Coachwith a factor per constant and a methodfare(base). - A helper
total(Coach[] seats, int base)adds up each seat's fare. - Print the total.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Move an order to its next state
Problem statement
An order status enum has PLACED, COOKING, PICKED_UP and DELIVERED. Write next() so each call moves one step forward and DELIVERED stays DELIVERED, then print the journey.
start at PLACED
Final: DELIVERED after 3 steps
Approach
- Use
ordinal()inside the enum to find the following constant invalues(). - Return
thisfor the last state. - Loop in main until DELIVERED.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Cheapest payment mode for an amount
Problem statement
Each payment mode has a fixed fee and a percentage fee: CARD ₹0 + 2%, NETBANKING ₹10 + 0%, WALLET ₹5 + 1%. For a ₹1500 purchase, find the cheapest mode.
₹1500
NETBANKING fee: ₹10
Approach
- Enum
Modewith two fields and a methodfee(amount). - Start with the first constant as the best, then loop over
values(), computing each fee once. - Keep the mode with the smallest fee and print it.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Check yourself
Quick revision
Lesson 10.4 in one screen
enum Status { PLACED, PACKED, SHIPPED }: a type with a fixed set of values, checked by javac.- Each constant is a
public static finalobject, created once; compare with==. - Built in:
values(),valueOf(name)(exact, elseIllegalArgumentException),name(),ordinal(),compareTo(). - Switch:
case SHIPPED ->; switch expressions must cover all constants. - Fields + private constructor + methods:
AC3(2.5),fare(base). Constant-specific bodies for different behaviour. - Implicitly extends
java.lang.Enum; can implement interfaces; can't be created withnew. - Use
EnumMap/EnumSet; storename(), notordinal().
Interview and viva questions
Why use an enum instead of int or String constants?
Type safety (only declared values compile), readable names in logs and debuggers, built-in values()/valueOf(), use in switch with completeness checks, and the ability to attach data and behaviour. With public static final int constants, any int is accepted and a typo in a String constant isn't noticed.
Can an enum have a constructor? Can it be public?
Yes, it can have constructors, fields and methods. The constructor is always private (implicitly); declaring it public is a compile error, because only the enum itself may create its constants.
Can an enum extend a class or implement an interface?
It can't extend a class, because it already extends java.lang.Enum and Java has single inheritance. It can implement any number of interfaces, for example enum Mode implements FeeRule.
How do you make a singleton with an enum?
enum Config { INSTANCE; … }. The JVM guarantees one instance, creation is thread-safe (class initialisation), and serialization and reflection can't create a second copy. Joshua Bloch's Effective Java recommends it as the simplest singleton.
What's the difference between name() and toString()?
name() is final and always returns the declared name. toString() returns the name by default but can be overridden for display, e.g. “Out for delivery”. Use name() for storage and valueOf, toString() for people.
Practice problems
Declare enums, switch on them and give constants data and behaviour. The last three are business tasks.
Print all days with their positions
Declare enum Day { MON, TUE, WED, THU, FRI, SAT, SUN } and print each day with its ordinal.
(none)
MON=0 TUE=1 WED=2 THU=3 FRI=4 SAT=5 SUN=6
Show solution and explanation
enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }
public class Main {
public static void main(String[] args) {
for (Day d : Day.values()) {
System.out.print(d + "=" + d.ordinal() + " ");
}
System.out.println();
}
}How it works: values() returns the constants in declaration order; ordinal() is the zero-based position.
Weekend or weekday?
Write isWeekend(Day d) using a switch expression and test SAT and WED.
SAT, WED
SAT: true WED: false
Show solution and explanation
enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }
public class Main {
static boolean isWeekend(Day d) {
return switch (d) {
case SAT, SUN -> true;
default -> false;
};
}
public static void main(String[] args) {
System.out.println("SAT: " + isWeekend(Day.SAT));
System.out.println("WED: " + isWeekend(Day.WED));
}
}How it works: a case can list several constants. default covers the remaining five.
Safe valueOf from user text
Convert " medium " and "huge" into Size values. Print MEDIUM for the first and Unknown size: huge for the second.
" medium ", "huge"
MEDIUM Unknown size: huge
Show solution and explanation
enum Size { SMALL, MEDIUM, LARGE }
public class Main {
static void read(String text) {
try {
System.out.println(Size.valueOf(text.trim().toUpperCase()));
} catch (IllegalArgumentException e) {
System.out.println("Unknown size: " + text.trim());
}
}
public static void main(String[] args) {
read(" medium ");
read("huge");
}
}How it works: valueOf needs the exact constant name, so normalise first and catch IllegalArgumentException for unknown values.
Planet weights
An enum Planet has a gravity factor relative to Earth: MERCURY 0.38, EARTH 1.0, JUPITER 2.53. Print the weight of a 60 kg person on each.
60 kg
MERCURY 22.8 kg EARTH 60.0 kg JUPITER 151.8 kg
Show solution and explanation
enum Planet {
MERCURY(0.38), EARTH(1.0), JUPITER(2.53);
private final double g;
Planet(double g) { this.g = g; }
double weight(double kg) { return kg * g; }
}
public class Main {
public static void main(String[] args) {
for (Planet p : Planet.values()) {
System.out.printf("%-8s %.1f kg%n", p, p.weight(60));
}
}
}How it works: each constant passes its own factor to the private constructor; the method uses this constant's field.
Calculator with constant-specific methods
Make enum Op with PLUS, MINUS and TIMES, each overriding apply(a, b). Print 8 op 2 for every op.
8, 2
8 PLUS 2 = 10 8 MINUS 2 = 6 8 TIMES 2 = 16
Show solution and explanation
enum Op {
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; } };
abstract int apply(int a, int b);
}
public class Main {
public static void main(String[] args) {
for (Op op : Op.values()) {
System.out.println("8 " + op + " 2 = " + op.apply(8, 2));
}
}
}How it works: the enum declares an abstract method and each constant supplies its own body. javac compiles each body into a small subclass (Op$1, Op$2, …).
Count orders by status
Given statuses of 6 orders, count how many are in each status using an EnumMap, printed in the enum's order.
PACKED, PLACED, SHIPPED, PACKED, DELIVERED, PACKED
{PLACED=1, PACKED=3, SHIPPED=1, DELIVERED=1}Show solution and explanation
import java.util.EnumMap;
enum Status { PLACED, PACKED, SHIPPED, DELIVERED }
public class Main {
public static void main(String[] args) {
Status[] orders = {Status.PACKED, Status.PLACED, Status.SHIPPED,
Status.PACKED, Status.DELIVERED, Status.PACKED};
EnumMap<Status, Integer> count = new EnumMap<>(Status.class);
for (Status s : orders) {
count.put(s, count.getOrDefault(s, 0) + 1);
}
System.out.println(count);
}
}How it works: an EnumMap is backed by an array indexed by ordinal, so it's fast and always iterates in declaration order.
Role-based permissions
Roles ADMIN, TEACHER and STUDENT each have a set of allowed actions (VIEW, EDIT_MARKS, DELETE_USER). Check whether a TEACHER may EDIT_MARKS and DELETE_USER.
TEACHER: EDIT_MARKS?, DELETE_USER?
TEACHER EDIT_MARKS: true TEACHER DELETE_USER: false
Show solution and explanation
import java.util.EnumSet;
enum Action { VIEW, EDIT_MARKS, DELETE_USER }
enum Role {
ADMIN(EnumSet.allOf(Action.class)),
TEACHER(EnumSet.of(Action.VIEW, Action.EDIT_MARKS)),
STUDENT(EnumSet.of(Action.VIEW));
private final EnumSet<Action> allowed;
Role(EnumSet<Action> allowed) { this.allowed = allowed; }
boolean can(Action a) { return allowed.contains(a); }
}
public class Main {
public static void main(String[] args) {
System.out.println("TEACHER EDIT_MARKS: " + Role.TEACHER.can(Action.EDIT_MARKS));
System.out.println("TEACHER DELETE_USER: " + Role.TEACHER.can(Action.DELETE_USER));
}
}How it works: each role carries its own EnumSet of actions; the permission rule is one method on the enum.
GST slab by category
Product categories have GST rates: ESSENTIAL 5%, STANDARD 18%, LUXURY 28%. Print the final price of ₹1000 items in each category.
₹1000
Essential: ₹1050 Standard: ₹1180 Luxury: ₹1280
Show solution and explanation
enum Category {
ESSENTIAL(5), STANDARD(18), LUXURY(28);
private final int gstPct;
Category(int gstPct) { this.gstPct = gstPct; }
int withGst(int price) { return price + price * gstPct / 100; }
@Override
public String toString() { return name().charAt(0) + name().substring(1).toLowerCase(); }
}
public class Main {
public static void main(String[] args) {
for (Category c : Category.values()) {
System.out.println(c + ": ₹" + c.withGst(1000));
}
}
}How it works: the rate lives with the category. Overriding toString() gives friendly text, while name() still returns ESSENTIAL for storage. (Simplified: real GST rates are set per HSN code and change by notification.)
Real-world scenario questions (asked in company interviews)
Enum questions in interviews are often about design: where should the rules live, and what happens when the list changes?
Replace status Strings
An order service stores status as a String and has bugs from inconsistent spelling. How would you refactor it?
The interviewer is checking: do you know when an enum is the right tool?
Show a strong answer
- Create
enum OrderStatus { PLACED, PACKED, SHIPPED, DELIVERED, CANCELLED }and change the field type. javac now flags every wrong value. - Convert at the edges only:
OrderStatus.valueOf(text.trim().toUpperCase())with error handling when reading requests;name()when writing to the database. - Move rules into the enum, e.g.
canMoveTo(OrderStatus next), so transitions are checked in one place.
Avoid: storing the ordinal in the database “to save space”. Adding a status in the middle silently corrupts old rows.
Fees spread across ten if-else chains
Fee logic for UPI, card, net banking and COD is copied in ten places as if (mode.equals("CARD")) …. Adding a new mode means editing all ten. What do you propose?
The interviewer is checking: putting behaviour on enum constants.
Show a strong answer
- An enum
PaymentModewith fields (fee percent, max amount) and a methodfee(amount), or constant-specific bodies when the formulas differ. - Callers do
mode.fee(amount). A new mode is one new constant; switch expressions elsewhere fail to compile until they handle it. - If fees change often or per merchant, keep the enum for the mode but load numbers from configuration.
Avoid: hard-coding values that the business changes weekly. Enums suit things that change with code releases.
A new constant breaks an old client
Your service adds RETURNED to OrderStatus. An older mobile app that deserialises the status with valueOf now crashes for returned orders. How do you prevent this?
The interviewer is checking: evolving enums across systems.
Show a strong answer
- Clients should handle unknown values: catch
IllegalArgumentExceptionand map to anUNKNOWNconstant (many JSON libraries have an option for this). - Roll out client support before the server starts sending new values; version the API if needed.
- Document the enum as “may grow” in the API contract.
Avoid: “never add constants”. Business changes; plan for it.
Exactly one configuration object
You need one shared AppConfig object for the whole application, created safely even if two threads ask for it at start-up. What's the simplest correct way in Java?
The interviewer is checking: knowledge of the enum singleton.
Show a strong answer
enum AppConfig { INSTANCE; private final Properties props = load(); … }.- The JVM creates
INSTANCEonce during class initialisation, which is thread-safe, and enums are protected against extra copies through reflection and serialization. - In larger apps, a dependency-injection container (e.g. Spring) usually manages singletons instead.
Avoid: double-checked locking without volatile. It's easy to get wrong.