This set of 12 Java enum exercises goes far beyond a simple list of constants, showing how much power enum actually carries in Java.
Each exercise includes a Problem Statement, Purpose, Hint, and a Solution with Explanation that shows exactly why each technique beats a plain switch statement or constant list.
- Also, See: Java Exercises with over 20+ topic-wise sets and 575+ coding questions to practice.
- Practice questions using our Online Java Compiler
+ Table of Contents (12 Exercises)
Table of contents
- Exercise 1: The Basic Days of the Week
- Exercise 2: Switch Case Routing
- Exercise 3: String Conversion and Validation
- Exercise 4: Enums with Custom Values (Fields)
- Exercise 5: Adding Behavior (Abstract Methods)
- Exercise 6: Text Formatting / Custom toString()
- Exercise 7: The Lookup Pattern
- Exercise 8: Implementing Interfaces
- Exercise 9: High-Performance Mapping with EnumMap
- Exercise 10: The Singleton Database Configuration
- Exercise 11: Complex State Machine
- Exercise 12: Bitwise Permissions with EnumSet
Exercise 1: The Basic Days of the Week
Problem Statement: Create an enum called Weekday containing the 7 days of the week. Write a main method that loops through all values (using .values()) and prints them out.
Purpose: This exercise helps you practice basic enum declaration and iterating over enum constants with values(), the foundation every other enum exercise builds on.
Expected Output:
MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY SUNDAY
▼ Hint
Weekday.values() returns an array of all constants in the order they were declared, so an enhanced for loop over that array prints them in the same order.
▼ Solution & Explanation
Explanation:
Weekday.values(): Returns an array containing every constant in the enum, in the exact order they were declared.for (Weekday day : Weekday.values()): Iterates over that array one constant at a time using an enhanced for loop.System.out.println(day): Implicitly calls the constant’stoString(), which by default returns the constant’s declared name.- Alternative: You could print the constants with
Arrays.stream(Weekday.values()).forEach(System.out::println)for a more compact, stream based version of the same loop.
Exercise 2: Switch Case Routing
Problem Statement: Create an enum called TrafficLight with values RED, YELLOW, and GREEN. Write a method getAction(TrafficLight light) that uses a switch statement to return a string: “Stop” for RED, “Prepare to stop” for YELLOW, and “Go” for GREEN.
Purpose: This exercise helps you practice how cleanly enums integrate with switch statements, since case labels can reference bare constant names without repeating the enum type.
Given Input: TrafficLight.RED
Expected Output: Stop
▼ Hint
Inside a switch (light) block, each case label only needs the bare constant name, like case RED:, since Java already knows the type being switched on.
▼ Solution & Explanation
Explanation:
switch (light): Switches directly on the enum value, letting each case label use a bare constant name instead of a fully qualified reference.case RED: return "Stop";: Matches the RED constant to its corresponding action string.default: throw new IllegalArgumentException(...): Protects against a new constant being added to the enum later without a matching case being added here.- Alternative: You could rewrite this with a modern switch expression:
return switch (light) { case RED -> "Stop"; case YELLOW -> "Prepare to stop"; case GREEN -> "Go"; };, which removes the need for explicitreturnstatements and break logic.
Exercise 3: String Conversion and Validation
Problem Statement: Create an enum called ShirtSize (SMALL, MEDIUM, LARGE, XLARGE). Take a string input from the user (e.g., “MEDIUM”). Convert it to the corresponding enum using valueOf(). Handle the IllegalArgumentException gracefully if the user enters an invalid size.
Purpose: This exercise helps you practice safely converting external strings, such as user input or data read from a file, into enum constants without letting a bad value crash the program.
Given Input: "medium"
Expected Output: Parsed size: MEDIUM
▼ Hint
valueOf()matches strings against constant names exactly, so normalize the input withtoUpperCase()first.- Wrap the
valueOf()call in atry-catchblock that catchesIllegalArgumentException. - Inside the catch block, print a friendly message instead of letting the exception propagate and crash the program.
▼ Solution & Explanation
Explanation:
ShirtSize.valueOf(input.toUpperCase()): Matches the normalized string against the declared constant names, returning the matching constant.catch (IllegalArgumentException e): Catches the exception thatvalueOf()throws whenever no constant matches the given string.- returning
nullon failure: Lets the caller detect an invalid size and decide how to respond, instead of the program terminating. - Alternative: You could build a
Set<String>of valid names once and check membership before callingvalueOf(), which avoids relying on exception handling for normal control flow.
Exercise 4: Enums with Custom Values (Fields)
Problem Statement: Create an enum called Coin with constants PENNY, NICKEL, DIME, and QUARTER. Assign a numeric value to each (e.g., Penny = 1, Nickel = 5). Add a getter method getValue() and a method to calculate the total value of an array of coins.
Purpose: This exercise helps you practice giving each enum constant its own field by passing arguments through an enum constructor, a pattern used whenever a constant needs to carry more data than just its name.
Given Input: Coin[] coins = {QUARTER, QUARTER, DIME, PENNY};
Expected Output: Total value: 61 cents
▼ Hint
- Give the enum a private constructor that takes an
int valueand stores it in aprivate finalfield. - Pass the numeric value to each constant when it is declared, like
PENNY(1). - Loop through the coin array and add each coin’s
getValue()to a running total.
▼ Solution & Explanation
Explanation:
Coin(int value): Runs once for each constant when the enum class is first loaded, assigning a fixed numeric value to that constant.private final int value: Keeps the assigned value immutable once a constant has been created.getValue(): Exposes the stored value through an ordinary getter, so other classes never need direct access to the field.- Alternative: You could store the values in a separate
Map<Coin, Integer>outside the enum, but keeping the value inside the enum itself guarantees every constant defines one and keeps related data together.
Exercise 5: Adding Behavior (Abstract Methods)
Problem Statement: Create an enum called Operation with constants PLUS, MINUS, TIMES, and DIVIDE. Define an abstract method double apply(double x, double y) inside the enum, and implement it for each constant so that Operation.PLUS.apply(4, 2) returns 6.0.
Purpose: This exercise helps you practice giving each enum constant its own behavior through a constant-specific class body, which removes the need for a switch statement whenever a new behavior is added.
Given Input: Operation.PLUS.apply(4, 2)
Expected Output: Result: 6.0
▼ Hint
- Declare
public abstract double apply(double x, double y);as a member of the enum, with no body. - After each constant, open a class body with curly braces and override
apply()inside it. - Remember to add a semicolon after the last constant, since the enum has additional members following it.
▼ Solution & Explanation
Explanation:
public abstract double apply(double x, double y);: Declares a method that every constant must implement, with no default body supplied by the enum itself.PLUS { @Override public double apply(...) {...} }: Attaches a constant-specific class body that supplies an implementation used only by PLUS.Operation.PLUS.apply(4, 2): Calls the implementation tied to that specific constant, returning 6.0.- Alternative: You could use a single
apply()method with a switch statement instead of per-constant bodies, though that requires editing the switch every time a new operation constant is added.
Exercise 6: Text Formatting / Custom toString()
Problem Statement: Create an enum UserRole (ADMIN, PREMIUM_USER, GUEST). Give each role a user-friendly string representation (e.g., “Administrator”, “Premium User”, “Guest”). Override the toString() method to return this friendly name.
Purpose: This exercise helps you practice decoupling an enum constant’s fixed declared name from the text you actually want to display to a user.
Given Input: UserRole.PREMIUM_USER
Expected Output: Role: Premium User
▼ Hint
Store a displayName field through the enum constructor just like the Coin exercise, then override toString() to return that field instead of the default constant name.
▼ Solution & Explanation
Explanation:
displayNamefield: Stores a friendly label separately from the constant’s fixed declared name.@Override public String toString(): Replaces the default implementation, which would otherwise return the raw constant name like PREMIUM_USER."Role: " + role: Implicitly callstoString()during string concatenation, so the friendly name appears in the output automatically.- Alternative: If you also need the original constant name somewhere, call
name()directly, since overridingtoString()does not change whatname()returns.
Exercise 7: The Lookup Pattern
Problem Statement: Create an enum HttpStatusCode (e.g., SUCCESS(200), BAD_REQUEST(400), NOT_FOUND(404), INTERNAL_SERVER_ERROR(500)). Implement a static method fromCode(int code) that searches and returns the matching enum, throwing an exception if the code doesn’t exist. Optimize this using a static Map<Integer, HttpStatusCode> cache built at startup.
Purpose: This exercise helps you practice the reverse-lookup pattern, converting a raw value like an HTTP status code back into its matching enum constant in constant time using a cache built once when the class loads.
Given Input: 404
Expected Output: Status: NOT_FOUND
▼ Hint
- Add a
private static final Map<Integer, HttpStatusCode>field to the enum. - Populate it inside a static initializer block that loops over
values()once when the class is loaded. - Have
fromCode(int code)read from that map instead of looping throughvalues()on every call.
▼ Solution & Explanation
Explanation:
static { ... }: Runs once when the enum class is loaded, populating the lookup map beforefromCode()is ever called.LOOKUP.put(status.code, status): Maps each numeric code to its matching constant so it can be retrieved directly instead of scanned for.LOOKUP.get(code): Performs a constant-time lookup, which stays fast even as the number of status codes grows.- Alternative: For a very small number of constants, looping through
values()and comparing codes directly insidefromCode()also works, though it re-scans every constant on every call instead of reading from a prebuilt map.
Exercise 8: Implementing Interfaces
Problem Statement: Create an interface Validator with a method boolean validate(String input). Create an enum StringValidator that implements Validator with constants like IS_NUMERIC, IS_ALPHA, and IS_EMAIL. Implement the validation logic for each.
Purpose: This exercise helps you practice using enums to implement interfaces, which turns each constant into a pluggable strategy that can be passed anywhere the interface type is expected.
Given Input: "user@example.com"
Expected Output: Valid email: true
▼ Hint
- Declare
enum StringValidator implements Validatorso every constant is required to provide avalidate()implementation. - Give each constant its own class body, similar to the
Operationexercise, and implementvalidate()with a regular expression check inside each one. - Use
String.matches()with a pattern suited to each validation rule (digits only, letters only, or a basic email shape).
▼ Solution & Explanation
Explanation:
enum StringValidator implements Validator: Lets every constant be treated as aValidator, so it can be passed to any code that only knows about the interface.input.matches("\\d+"): Checks the whole input string against a regular expression tailored to that specific validation rule.- constant-specific bodies: Gives each constant its own
validate()implementation, the same technique used for behavior in theOperationexercise. - Alternative: You could keep
validate()as an abstract method declared directly on the enum without implementing an interface, though implementingValidatorlets other classes depend only on the interface instead of the concrete enum type.
Exercise 9: High-Performance Mapping with EnumMap
Problem Statement: Create an enum ProjectStatus (BACKLOG, IN_PROGRESS, TESTING, DONE). Write a program that takes a list of tasks (each having a status) and groups them by their status using an EnumMap<ProjectStatus, List<Task>>.
Purpose: This exercise helps you practice using EnumMap, which stores entries in the enum’s declaration order and avoids hashing overhead entirely, making it a better fit than HashMap whenever the keys are enum constants.
Expected Output:
IN_PROGRESS: 2 task(s) TESTING: 1 task(s) DONE: 1 task(s)
▼ Hint
- Create the map with
new EnumMap<>(ProjectStatus.class), sinceEnumMapneeds the enum’s class to size itself internally. - Loop through the task list and use
computeIfAbsent()to create a new list the first time a status is seen. - Add each task to the list stored under its status, then iterate the map’s
entrySet()to print the results.
▼ Solution & Explanation
Explanation:
new EnumMap<>(ProjectStatus.class): Creates a map that always iterates in the enum’s declaration order and uses the constant’s ordinal internally instead of hashing.computeIfAbsent(task.getStatus(), k -> new ArrayList<>()): Creates a new list the first time a status appears, then reuses that same list for every task that follows with the same status.- iteration order: BACKLOG never appears in the output because no task uses it, while IN_PROGRESS, TESTING, and DONE print in their declared order rather than insertion order.
- Alternative: A regular
HashMap<ProjectStatus, List<Task>>would work functionally the same, but it hashes every key and does not guarantee a predictable iteration order the wayEnumMapdoes.
Exercise 10: The Singleton Database Configuration
Problem Statement: Create an enum called DatabaseConnection with a single instance called INSTANCE. Give it fields like url, username, and password. Add a method connect().
Purpose: This exercise helps you practice the Joshua Bloch “Enum Singleton” pattern, which the Java language itself guarantees to be thread-safe and protected against reflection and serialization attacks, unlike a hand-written Singleton class.
Expected Output: Connecting to jdbc:mysql://localhost:3306/mydb as admin
▼ Hint
Declare a single constant, INSTANCE, and Java guarantees exactly one instance of the enum ever gets created, so you can add ordinary fields and methods to it just like any other enum.
▼ Solution & Explanation
Explanation:
enum DatabaseConnection { INSTANCE; ... }: Defines exactly one constant, so only a single instance of the enum can ever exist in the program.- thread-safe creation: Java guarantees enum instances are created once, safely, when the class is loaded, with no extra synchronization code required.
DatabaseConnection.INSTANCE.connect(): Calls the method on the single shared instance from anywhere in the program.- Alternative: A traditional Singleton built with a private constructor and a static
getInstance()method achieves a similar result, but it takes extra code to guard against reflection and serialization, protection the enum approach gets for free.
Exercise 11: Complex State Machine
Problem Statement: Create an enum OrderState representing an e-commerce order: PLACED, PAID, SHIPPED, DELIVERED, CANCELLED. Add logic that returns the valid next states for a given state (e.g., PLACED can move to PAID or CANCELLED). Prevent invalid transitions (e.g., SHIPPED cannot go straight to CANCELLED).
Purpose: This exercise helps you practice using an enum to build a lightweight state machine, where each constant knows exactly which states it is allowed to move to next.
Given Input: current state PLACED, target state PAID
Expected Output: New state: PAID
▼ Hint
- Add a method that returns an
EnumSet<OrderState>of valid next states for the current constant, using a switch onthis. - Give DELIVERED and CANCELLED an empty set, since they represent final states with no further transitions.
- Write a second method that checks whether a target state is inside that valid set, throwing an exception when it is not.
▼ Solution & Explanation
Explanation:
getValidTransitions(): Uses a switch onthisto return the set of states the current constant is allowed to move to next.EnumSet.of(PAID, CANCELLED): Builds a small, memory efficient set containing only the given constants as valid targets.transitionTo(target): Checks the target against the valid set and throwsIllegalStateExceptionfor a disallowed move, such as jumping from SHIPPED straight to CANCELLED.- Alternative: Instead of a switch inside the enum, you could store each constant’s valid transitions in a static
EnumMap<OrderState, EnumSet<OrderState>>built once, which separates the transition rules from the enum’s own code.
Exercise 12: Bitwise Permissions with EnumSet
Problem Statement: Create an enum Permission (READ, WRITE, EXECUTE, DELETE). Write a User class that holds an EnumSet<Permission>. Implement methods to grantPermission(Permission p), revokePermission(Permission p), and hasPermission(Permission p).
Purpose: This exercise helps you practice using EnumSet, which stores membership as a bit vector internally, giving you the speed of manual bitwise flags with a fully type-safe, readable API.
Expected Output:
Has READ: true Has WRITE: false
▼ Hint
- Initialize the field with
EnumSet.noneOf(Permission.class)so the user starts with no permissions. - Implement
grantPermission()andrevokePermission()by delegating to the set’s ownadd()andremove()methods. - Implement
hasPermission()using the set’scontains()method.
▼ Solution & Explanation
Explanation:
EnumSet.noneOf(Permission.class): Creates an empty set sized to match the number of constants inPermission, backed internally by a bit vector for very fast operations.grantPermission()/revokePermission(): Simply delegate to the underlying set’sadd()andremove()methods.hasPermission(): Checks membership withcontains(), which runs in constant time becauseEnumSetstores membership as bits instead of scanning a list.- Alternative: You could represent permissions with a plain
intand manual bitwise operators like|and&, which is close to howEnumSetworks internally, butEnumSetgives you the same performance with a type-safe, readable API.

Leave a Reply