Exploring Java Enumerations: A Comprehensive Guide

Introduction

link to this section

Java Enumerations, also known as Enums, are a particular data type that allow us to define a finite set of values (constants). They can be used when we know all possible values at compile time, such as the days of the week, directions, colors, states of a process, and many more. Java Enums were introduced in Java 5.0, bringing the functionality of this powerful construct to the Java ecosystem. This blog post is designed to delve into the concept of Java Enums, covering their creation, usage, and features, along with some notable examples.

Basic Syntax of Java Enums

link to this section

The simplest form of an Enum in Java can be defined as follows:

public enum Day { 
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY 
} 

Here, Day is an enumeration of the seven days of the week. Each Enum constant is implicitly static and final, so the uppercase letters are used by convention.

Using Enums

link to this section

Java Enums can be used in a switch statement, a loop, or even as an argument for a method. Let's see a simple example of an Enum in a switch statement:

public class Test { 
    Day day; 
    
    public Test(Day day) { 
        this.day = day; 
    } 
    
    public void tellItLikeItIs() { 
        switch (day) { 
            case MONDAY: 
                System.out.println("Mondays are hard."); 
                break; 
            case FRIDAY: 
                System.out.println("Fridays are better."); 
                break;  
            case SATURDAY: 
                System.out.println("Weekends are best."); 
                break; 
            case SUNDAY: 
                System.out.println("Weekends are best."); 
                break; 
            default: 
                System.out.println("Midweek days are so-so."); 
                break; 
        } 
    } 
        
    public static void main(String[] args) { 
        Test firstDay = new Test(Day.MONDAY); 
        firstDay.tellItLikeItIs(); // Output: Mondays are hard. 
    } 
} 

Enum Methods

link to this section

The Enum in Java provides several inbuilt methods, including values() , valueOf() , ordinal() , and more.

  1. values() : This method returns an array containing all of the values of the enum in the order they are declared.

    Day[] days = Day.values(); 
    for (Day day : days) { 
        System.out.println(day); 
    } 
  2. valueOf(String name) : This method returns the enum constant of the specified string value, if it exists.

    Day day = Day.valueOf("MONDAY"); 
    System.out.println(day); // Output: MONDAY 
  3. ordinal() : This method returns the position of the enum constant (0-based).

    System.out.println(Day.MONDAY.ordinal()); // Output: 0 

Advanced Features of Java Enums

link to this section
  1. Enum Constructors: Enum in Java can have constructors, which are called separately for each constant at the time of enum class loading. The constructor can only be private or package scope.

    public enum Day { 
        MONDAY("M"), TUESDAY("T"), WEDNESDAY("W"), THURSDAY("T"), FRIDAY("F"), SATURDAY("S"), SUNDAY("S"); 
        
        private String abbreviation; 
        
        Day(String abbreviation) { 
            this.abbreviation = abbreviation; 
        } 
        
        public String getAbbreviation() { 
            return this.abbreviation; 
        } 
    } 
  2. Enum Methods: Besides the constructor, we can also define methods in an enum.

    public enum Day { 
        // ... enum constants 
        // ... constructor 
        
        public boolean isWeekend() { 
            return (this == SATURDAY || this == SUNDAY); 
        } 
    } 
  3. Enum and Polymorphism: Each enum constant can implement a behavior specific to that constant.

    public enum Operation { 
        PLUS { 
            public int apply(int x, int y) { return x + y; } 
        }, 
        MINUS { 
            public int apply(int x, int y) { return x - y; } 
        }, 
        MULTIPLY { 
            public int apply(int x, int y) { return x * y; } 
        }, 
        DIVIDE { 
            public int apply(int x, int y) { return x / y; } 
        }; 
    
        public abstract int apply(int x, int y); 
    } 

Implementing Interfaces in Enums

link to this section

Just like classes, enums can also implement interfaces. This allows us to use polymorphism principles and make the code more flexible and reusable.

public interface Activity { 
    void performAction(); 
} 

public enum Day implements Activity { 
    MONDAY { 
        public void performAction() { 
            System.out.println("Start of the work week."); 
        } 
    },     
    FRIDAY { 
        public void performAction() { 
            System.out.println("End of the work week. Ready for the weekend!"); 
        } 
    }, 
    // other days ; 
} 

EnumSet and EnumMap

link to this section

Java provides EnumSet and EnumMap classes specifically for use with enum types. EnumSet is a high-performance Set implementation and EnumMap is a Map implementation for use with enum keys.

EnumSet<Day> weekend = EnumSet.of(Day.SATURDAY, Day.SUNDAY); 
System.out.println(weekend); // prints [SATURDAY, SUNDAY] 
EnumMap<Day, String> map = new EnumMap<>(Day.class); 
map.put(Day.MONDAY, "Start of the work week"); 
System.out.println(map); // prints {MONDAY=Start of the work week} 

Enum Constants and Singleton Pattern

link to this section

Enum constants are singletons, meaning that there's only one instance of each enum constant and they can be compared using the == operator.

if (Day.MONDAY == Day.MONDAY) { 
    System.out.println("Same instance"); // this will be printed 
} 

Thread Safety

link to this section

Enums are inherently thread-safe. All of their members are final and they're guaranteed to have a single instance, so they can be safely used in a multi-threaded environment.

Reflection and Enums

link to this section

Although it's possible to use reflection to invoke methods on enum instances, it's not possible to instantiate an enum using reflection, which is another proof of their immutability.

Enums in Switch Statements and Type Safety

link to this section

Enums can be used in switch-case statements. This helps in writing cleaner code and also avoids common bugs, like typo errors, that would have gone unnoticed in case of string constants.

Day day = Day.MONDAY; 
switch (day) { 
    case MONDAY: 
        System.out.println("Mondays are hard."); 
        break; 
    // other cases 
} 

The fact that you cannot pass a wrong value, one that is not defined in the enum, to the switch statement ensures type safety.

Enums are Serializable

link to this section

Since Enum implements the Serializable interface, it's automatically Serializable. However, only the name of the enum constant is serialized. The field values are not serialized, as they are considered to be per-constant data and not per-instance data.

Equality and Comparison

link to this section

Enums can be compared for equality using both == and equals() . Both will give the same result because there's only one object for each enum constant.

if (Day.MONDAY == Day.MONDAY) { 
    System.out.println("True"); // prints True 
} 
if (Day.MONDAY.equals(Day.MONDAY)) { 
    System.out.println("True"); // prints True 
} 

toString() and name()

link to this section

All enums automatically have toString() and name() methods. The toString() method returns the name of this enum constant, exactly as declared in its enum declaration. The name() method also does the same thing. However, toString() can be overridden to provide custom logic, whereas name() can't be.

public enum Day { 
    MONDAY { 
        @Override 
        public String toString() { 
            return "It's Monday!"; 
        } 
    }, 
    // other days 
    ; 
} 

Use of Enum in Annotations

link to this section

Java Enums can also be used in annotations. For instance, you can define an annotation where one of the elements is an enum type.

public @interface Schedule { 
    Day day(); 
} 

@Schedule(day = Day.MONDAY) 
public void doTask() { // code here } 

Use of Enum in Collections

link to this section

Enums can be used as elements in collections like lists, sets, or as keys in maps, providing type-safety and reducing bugs caused by passing inappropriate values.

List<Day> daysList = new ArrayList<>(); 
daysList.add(Day.MONDAY); 
daysList.add(Day.TUESDAY); 

Nested Enum

link to this section

Enums can be nested within other classes or within other enums. However, they implicitly have a static modifier, so they cannot be nested within methods.

public class OuterClass { 
    enum NestedEnum { 
        A, B, C 
    } 
} 

Conclusion

link to this section

To sum up, enums in Java are much more powerful than simple lists of constants. They can have fields, constructors, and methods; they can implement interfaces; and they have built-in thread safety. The strong typing ensures that enums are used where they're supposed to be used. Understanding and making use of these features can greatly enhance the readability, maintainability, and robustness of your code.