Introduction
Java Enums are a class specifically designed to represent a set of constants. Enums are used when you need a set of values which do represent some kind of numeric or text data, and this set is limited to certain values. You should think of them as a mechanism to encode a set of related values in a typesafe manner. But why should they be so crucial?
Enums allow to provide more descriptive names to the set of related constants and benefit of compile time type checking. This is very much beneficial to your code in terms of readability and less possibility of introducing errors compared to the traditional constant definitions.
Basics of Java Enums
Defining Enums
In Java, enums are specialized classes and are declared using the enum
keyword. They can have constructors, methods, and fields like any other classes, however, they are often less complex.
Syntax of Enums
The basic syntax of defining an enum is straightforward:
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
}
This creates an enum called Day
with seven constants.
Working with Enums
Declaring an Enum
To use an enum, you simply declare a variable of the enum type:
Day today = Day.MONDAY;
Using Enums in Switch Statements
Enums work seamlessly with switch statements, providing a clean and readable way to control flow based on enum values:
switch (today) {
case MONDAY:
System.out.println("Start of the work week");
break;
case FRIDAY:
System.out.println("End of the work week");
break;
case SUNDAY:
System.out.println("Weekend!");
break;
default:
System.out.println("Midweek day");
break;
}
Enum Methods
Enums come with some built-in methods that are quite useful.
values() Method
The values()
method returns an array of all the constants of the enum type:
for (Day day : Day.values()) {
System.out.println(day);
}
valueOf() Method
The valueOf()
method returns the enum constant of the specified string value if it exists:
Day day = Day.valueOf("MONDAY");
System.out.println(day); // Outputs: MONDAY
Advanced Features of Enums
Enums with Fields and Methods
Enums can have fields and methods just like regular classes. This can be very powerful for adding more functionality:
public enum Planet {
MERCURY(3.303e+23, 2.4397e6),
VENUS(4.869e+24, 6.0518e6);
private final double mass; // in kilograms
private final double radius; // in meters
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
public double mass() { return mass; }
public double radius() { return radius; }
}
Enums with Constructors
Enums can have constructors, which are called automatically when the constants are created:
public enum Size {
SMALL("S"), MEDIUM("M"), LARGE("L");
private String abbreviation;
Size(String abbreviation) {
this.abbreviation = abbreviation;
}
public String getAbbreviation() {
return abbreviation;
}
}
To demonstrate how you can use the Size
Enum:
public class SizeExample {
public static void main(String[] args) {
Size size1 = Size.SMALL;
Size size2 = Size.MEDIUM;
Size size3 = Size.LARGE;
System.out.println("Size: " + size1 + ", Abbreviation: " + size1.getAbbreviation());
System.out.println("Size: " + size2 + ", Abbreviation: " + size2.getAbbreviation());
System.out.println("Size: " + size3 + ", Abbreviation: " + size3.getAbbreviation());
}
}
The Output
Size: SMALL, Abbreviation: S
Size: MEDIUM, Abbreviation: M
Size: LARGE, Abbreviation: L
EnumSet and EnumMap
EnumSet
EnumSet
is a specialized set implementation for use with enum types. It is highly efficient:
Let’s consider an Enum representing the days of the week:
public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
}
Using EnumSet to create a set of weekdays:
import java.util.EnumSet;
public class EnumSetExample {
public static void main(String[] args) {
// Create an EnumSet with all days of the week
EnumSet<Day> allDays = EnumSet.allOf(Day.class);
System.out.println("All Days: " + allDays);
// Create an EnumSet with only weekdays
EnumSet<Day> weekdays = EnumSet.range(Day.MONDAY, Day.FRIDAY);
System.out.println("Weekdays: " + weekdays);
// Create an EnumSet with only weekend days
EnumSet<Day> weekends = EnumSet.of(Day.SATURDAY, Day.SUNDAY);
System.out.println("Weekends: " + weekends);
}
}
The Output
All Days: [MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]
Weekdays: [MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY]
Weekends: [SATURDAY, SUNDAY]
EnumMap
EnumMap
is a specialized map implementation for use with enum keys. It is very efficient and ensures type safety:
Using the same Day Enum, let’s create an EnumMap to map days of the week to activities:
import java.util.EnumMap;
public class EnumMapExample {
public static void main(String[] args) {
// Create an EnumMap with Day as the key type
EnumMap<Day, String> dayActivities = new EnumMap<>(Day.class);
// Add activities for each day
dayActivities.put(Day.MONDAY, "Go to the gym");
dayActivities.put(Day.TUESDAY, "Attend meeting");
dayActivities.put(Day.WEDNESDAY, "Work on project");
dayActivities.put(Day.THURSDAY, "Meet friends");
dayActivities.put(Day.FRIDAY, "Watch movie");
dayActivities.put(Day.SATURDAY, "Go hiking");
dayActivities.put(Day.SUNDAY, "Rest");
// Print the activities for each day
for (Day day : Day.values()) {
System.out.println(day + ": " + dayActivities.get(day));
}
}
}
The Output
MONDAY: Go to the gym
TUESDAY: Attend meeting
WEDNESDAY: Work on project
THURSDAY: Meet friends
FRIDAY: Watch movie
SATURDAY: Go hiking
SUNDAY: Rest
Benefits of Using EnumSet and EnumMap
- Performance: Both
EnumSet
andEnumMap
are highly efficient in terms of performance and memory usage. - Type Safety: These classes only work with Enums, ensuring that only valid Enum constants are used.
- Readability: Using Enums with these collections makes the code more readable and easier to maintain.
- Natural Ordering:
EnumMap
maintains the natural order of the Enum constants, making the data more predictable.
Enum Implementing Interfaces
Enums can implement interfaces, which allows them to be used in more flexible ways:
Create an interface that declares the method to be implemented by the Enums.
public interface Payment {
void processPayment(double amount);
}
Create an Enum that implements the Payment
interface. Each constant will provide its own implementation of the processPayment
method.
public enum PaymentMethod implements Payment {
CREDIT_CARD {
@Override
public void processPayment(double amount) {
System.out.println("Processing credit card payment of $" + amount);
}
},
PAYPAL {
@Override
public void processPayment(double amount) {
System.out.println("Processing PayPal payment of $" + amount);
}
},
CASH {
@Override
public void processPayment(double amount) {
System.out.println("Processing cash payment of $" + amount);
}
};
}
Use the PaymentMethod
Enum to process payments through different methods.
public class PaymentProcessor {
public static void main(String[] args) {
processPayment(PaymentMethod.CREDIT_CARD, 100.0);
processPayment(PaymentMethod.PAYPAL, 200.0);
processPayment(PaymentMethod.CASH, 50.0);
}
public static void processPayment(Payment paymentMethod, double amount) {
paymentMethod.processPayment(amount);
}
}
The Output
Processing credit card payment of $100.0
Processing PayPal payment of $200.0
Processing cash payment of $50.0
Practical Applications of Enums
Using Enums for Constants
Enums are perfect for replacing groups of constants. This makes the code more readable and maintainable:
public enum Status {
SUCCESS, FAILURE, PENDING;
}
Enums can also be used to represent more complex sets of constants, such as HTTP status codes.
public enum HttpStatus {
OK(200, "OK"),
NOT_FOUND(404, "Not Found"),
INTERNAL_SERVER_ERROR(500, "Internal Server Error");
private final int code;
private final String description;
HttpStatus(int code, String description) {
this.code = code;
this.description = description;
}
public int getCode() {
return code;
}
public String getDescription() {
return description;
}
}
Using the Enum in Code:
public class HttpResponseHandler {
public static void handleResponse(HttpStatus status) {
System.out.println("Status Code: " + status.getCode());
System.out.println("Description: " + status.getDescription());
}
public static void main(String[] args) {
handleResponse(HttpStatus.OK);
handleResponse(HttpStatus.NOT_FOUND);
}
}
The Output
Status Code: 200
Description: OK
Status Code: 404
Description: Not Found
Enums in State Machines
Enums are often used to implement state machines because they offer clarity and type safety. A state machine is a model of computation that consists of a finite number of states, transitions between those states, and actions. Using Enums for states ensures that the states are clearly defined and that only valid transitions are allowed.
Why Enums are Useful for State Machines
- Clarity : Enums provide clear, named constants for each state, making the code easy to read and understand.
- Type Safety: The compiler ensures that only valid Enum values are used, reducing the chance of errors.
Example of a State Machine Using Enums
Let’s say we are modeling a simple traffic light system. The traffic light can be in one of three states: RED, YELLOW, or GREEN. The transitions between these states follow a specific order: RED → GREEN → YELLOW → RED.
Create an Enum to represent the states of the traffic light.
public enum TrafficLightState {
RED,
GREEN,
YELLOW;
}
Implement the state machine logic using a class. This class will have a current state and a method to transition to the next state.
public class TrafficLight {
private TrafficLightState currentState;
public TrafficLight() {
currentState = TrafficLightState.RED; // Initial state
}
public void nextState() {
switch (currentState) {
case RED:
currentState = TrafficLightState.GREEN;
break;
case GREEN:
currentState = TrafficLightState.YELLOW;
break;
case YELLOW:
currentState = TrafficLightState.RED;
break;
}
}
public TrafficLightState getCurrentState() {
return currentState;
}
}
Use the TrafficLight
class to simulate the traffic light state transitions.
public class Main {
public static void main(String[] args) {
TrafficLight trafficLight = new TrafficLight();
for (int i = 0; i < 6; i++) {
System.out.println("Current State: " + trafficLight.getCurrentState());
trafficLight.nextState();
}
}
}
The Output
Current State: RED
Current State: GREEN
Current State: YELLOW
Current State: RED
Current State: GREEN
Current State: YELLOW
Like this example we can use emums in state management.
Common Mistakes with Enums
One common mistake is trying to instantiate enums with the new
keyword, which is not allowed. Also, be careful with the valueOf()
method, as it throws an exception if the provided name does not match any constant.
Performance Considerations
Efficiency of Enums in Java
Enums are extremely efficient both in terms of memory and speed. They are singleton, meaning there is only one instance of each constant, reducing memory overhead.
Best Practices for Using Enums
Guidelines for Effective Use
- Use enums for fixed sets of constants.
- Take advantage of the type safety provided by enums.
- Add methods to enums to encapsulate related behavior.
Enums vs. Traditional Constants
Comparing Enums with Static Final Constants
Enums provide several advantages over static final constants:
- Type safety.
- Built-in methods.
- Improved readability and maintainability.
Enums and Annotations
Using Annotations with Enums
Annotations can be applied to enums, just as to any other class object, which is useful for tools and frameworks:
Here are some examples of usin built in annotations with enums.
- @Deprecated
This annotation marks an Enum constant as deprecated, indicating that it should no longer be used and may be removed in the future.
public enum Day {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
@Deprecated
SATURDAY,
SUNDAY
}
In this example, SATURDAY is marked as deprecated. If you use this constant in your code, the compiler will generate a warning.
- @Override
While not typically used with Enums, this annotation is useful when you override methods from a superclass or implement methods from an interface in a class. It ensures that you are correctly overriding a method.
public enum Day {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY;
@Override
public String toString() {
return name().toLowerCase();
}
}
Here, we override the toString method to return the Enum constant name in lowercase. The @Override annotation ensures this method correctly overrides the toString method from the Object class.
- @SuppressWarnings
This annotation tells the compiler to ignore specific warnings for the annotated element.
@SuppressWarnings("deprecation")
public class EnumExample {
public static void main(String[] args) {
Day day = Day.SATURDAY;
System.out.println(day);
}
}
In this example, the @SuppressWarnings(“deprecation”) annotation suppresses the warning generated by using the deprecated SATURDAY constant.
Like this you can define custom annotations and use with enums just like other components in Java (Classes , Attribute , etc.)
Conclusion
Java Enums are a powerful feature that offers type safety, readability, and maintainability. Whether you’re defining a set of constants, creating a state machine, or implementing complex logic, enums can make your code cleaner and more robust.