Java enum tutorial

Java enum, also called Java enumeration type, is a type whose fields consist of a fixed set of constants. The very purpose of enum is to enforce compile time type safety. enum keyword is reserved keyword in Java.

We should use enum when we know all possible values of a variable at compile time or design time, though we can add more values in future as and when we identify them. In this java enum tutorial, we will learn what enums are and what problems they solve?

1. enum in Java

Enumerations (in general) are generally a set of related constants. They have been in other programming languages like C++ from beginning. After JDK 1.4, Java designers decided to support it in Java also, and it was officially released in JDK 1.5 release.

Enumeration in Java is supported by keyword enum. enums are a special type of class that always extends java.lang.Enum.

1.1. enum is reserved keyword

enum in Java is reserved keyword. It means you cannot define a variable of name enum. e.g. It will result in compile time error "invalid VariableDeclaratorId".

enum is reserved keyword

1.2. Java enum declaration

A simple example to create enum. As we know, generally we deal with four directions in daily life. Their names, angles and other properties are fixed. So, in programs, we can create enum for them. Syntax to create an enum is as below:

public enum Direction 
{
   EAST, WEST, NORTH, SOUTH;
}

Logically, each enum is an instance of enum type itself. So given enum can be seen as below declaration. JVM internally adds ordinal and value methods to this class which we can call while working with enum.

final class Direction extends Enum<Direction> 
{
    public final static Direction EAST = new Direction();
    public final static Direction WEST = new Direction();
    public final static Direction NORTH = new Direction();
    public final static Direction SOUTH = new Direction();
}

1.3. Java enum example

We can use the enum just like we use final static class fields.

public class EnumExample 
{
    public static void main(String[] args) 
    {
        Direction north = Direction.NORTH;
        
        System.out.println(north);        //Prints NORTH
    }
}

1.4. enum ordinal()

The ordinal() method returns the order of an enum instance. It represents the sequence in the enum declaration, where the initial constant is assigned an ordinal of '0'. It is very much like array indexes.

It is designed for use by sophisticated enum-based data structures, such as EnumSet and EnumMap.

Direction.EAST.ordinal();     //0

Direction.NORTH.ordinal();    //2

1.5. enum values() and valueOf()

The enum values() method returns all the enum values in an enum array.

Direction[] directions = Direction.values();

for (Direction d : directions) {
    System.out.println(d);
}

//Output:

EAST
WEST
NORTH
SOUTH

The enum valueOf() method helps to convert string to enum instance.

Direction east = Direction.valueOf("EAST");
        
System.out.println(east);

//Output:

EAST

1.6. enum naming convention

By convention, enums are constants. In Java, constants are defined in all UPPER_CASE letters. This follows are enums also.

  • enum name should be in title case (same as class names).
  • enum fields should be in all UPPER CASE (same as static final constants).

2. enum constructors

By default, enums don’t require constructor definitions and their default values are always the string used in the declaration. Though, you can give define your own constructors to initialize the state of enum types.

For example, we can add angle attribute to direction. All directions have some angle. So let’s add them.

public enum Direction 
{
    // enum fields
    EAST(0), WEST(180), NORTH(90), SOUTH(270);

    // constructor
    private Direction(final int angle) {
        this.angle = angle;
    }

    // internal state
    private int angle;

    public int getAngle() {
        return angle;
    }
}

If we want to access angle for any direction, we can make a simple method call in enum field reference.

Direction north = Direction.NORTH;
        
System.out.println( north );                      //NORTH

System.out.println( north.getAngle() );           //90

System.out.println( Direction.NORTH.getAngle() ); //90

3. enum methods

Remember that enum is basically a special class type, and can have methods and fields just like any other class. You can add methods which are abstract as well as concrete methods as well. Both methods are allowed in enum.

3.1. concrete methods in enum

Adding a concrete method in enum is similar to add same method in any other class. You can use any access specifier e.g. public, private or protected. You can return values from enum methods or simply use them to perform internal logic.

public enum Direction 
{
    // enum fields
    EAST, WEST, NORTH, SOUTH;
    
    protected String printDirection() 
    {
        String message = "You are moving in " + this + " direction";
        System.out.println( message );
        return message;
    }
}

You can call printDirection() method as simple method calls on enum instance.

Direction.NORTH.printDirection(); //You are moving in NORTH direction
        
Direction.EAST.printDirection();  //You are moving in EAST direction

3.2. abstract methods in enum

We can add abstract method in enums. In this case, we must implement the abstract method at each enum field, individually.

public enum Direction 
{
    // enum fields
    EAST {
        @Override
        public String printDirection() {
            String message = "You are moving in east. You will face sun in morning time.";
            return message;
        }
    },
    WEST {
        @Override
        public String printDirection() {
            String message = "You are moving in west. You will face sun in evening time.";
            return message;
        }
    },
    NORTH {
        @Override
        public String printDirection() {
            String message = "You are moving in north. You will face head in daytime.";
            return message;
        }
    },
    SOUTH {
        @Override
        public String printDirection() {
            String message = "You are moving in south. Sea ahead.";
            return message;
        }
    };

    public abstract String printDirection();
}

Re-run above example.

Direction.NORTH.printDirection(); //You are moving in north. You will face head in daytime.
        
Direction.EAST.printDirection();  //You are moving in east. You will face sun in morning time.

You can enforce a contract for all enums to be created in this way. It can serve as template for enum creation.

For example, If we want that each enum type of Direction should be able to print the direction name with a custom message when needed. This can be done by defining a abstract method inside Direction, which each enum has to override. In future, in any more directions are added (really?), then we must add a custom message as well.

4. enum inheritance

As mentioned earlier, enums extends Enum class. java.lang.Enum is an abstract class. This is the common base class of all Java enumeration types.

public abstract class Enum<E extends Enum<E>> 
					extends Object 
					implements Comparable<E>, Serializable {
    
}

It means that all enums are comparable and serializable implicitly. Also, all enum types in Java are singleton by default.

As noted all enums extends java.lang.Enum, so enum cannot extend any other class because Java does not support multiple inheritance this way. But enums can implement any number of interfaces.

5. Compare enums

All enums are by default comparable and singletons as well. It means you can compare them with equals() method, even with "==" operator.

Direction east = Direction.EAST;
Direction eastNew = Direction.valueOf("EAST");

System.out.println( east == eastNew );           //true
System.out.println( east.equals( eastNew ) );    //true

You can compare enum types using '==' operator or equals() method, because enums are singlton and comparable by default.

6. Enum collections – EnumSet and EnumMap

Two classes have been added to java.util package in support of enums – EnumSet (a high-performance Set implementation for enums; all members of an enum set must be of the same enum type) and EnumMap (a high-performance Map implementation for use with enum keys).

6.1. java.util.EnumSet

EnumSet class is defined as follows:

public abstract class EnumSet<E extends Enum<E>> 
						extends AbstractSet<E> 
						implements Cloneable, Serializable {
  
}

A specialized Set implementation for use with enum types. All of the elements in an enum set must come from a single enum type that is specified, explicitly or implicitly, when the set is created.

6.1.1. EnumSet Example

public class Test 
{
   public static void main(String[] args) 
   {
     Set enumSet = EnumSet.of(  Direction.EAST,
                                Direction.WEST,
                                Direction.NORTH,
                                Direction.SOUTH
                              );
   }
 }

Like most collection implementations EnumSet is not synchronized. If multiple threads access an enum set concurrently, and at least one of the threads modifies the set, it should be synchronized externally.

null elements are not permitted. Also, these sets guarantee the ordering of the elements in the set based on their order in the enumeration constants is declared. Performance and memory benefits are very high in comparison to a regular set implementation.

6.2. java.util.EnumMap

EnumMap is declared as:

public class EnumMap<K extends Enum<K>,V> extends AbstractMap<K,V> implements Serializable, Cloneable {
  
}

A specialized Map implementation for use with enum type keys. Also, all of the keys in an enum map must come from a single enum type that is specified, explicitly or implicitly, when the map is created.

Like EnumSet, null keys are not permitted and is not synchronized as well.

6.2.1. EnumMap Example

public class Test 
{
  public static void main(String[] args)
  {
    //Keys can be only of type Direction
    Map enumMap = new EnumMap(Direction.class);

    //Populate the Map
    enumMap.put(Direction.EAST, Direction.EAST.getAngle());
    enumMap.put(Direction.WEST, Direction.WEST.getAngle());
    enumMap.put(Direction.NORTH, Direction.NORTH.getAngle());
    enumMap.put(Direction.SOUTH, Direction.SOUTH.getAngle());
  }
}

7. Summary

  1. enums are implicitly final subclasses of java.lang.Enum class
  2. if an enum is a member of a class, it’s implicitly static
  3. new keyword can not be used to intialize an enum, even within the enum type itself
  4. name() and valueOf() methods simply use the text of the enum constants, while toString() method may be overridden to provide any content, if desired
  5. for enum constants, equals() and "==" evaluates to same result, and can be used interchangeably
  6. enum constants are implicitly public static final
  7. the order of appearance of list of enum constants is called their “natural order“, and defines the order used by other items as well : compareTo() method, iteration order of values in EnumSet, EnumSet.range().
  8. Enum constructors should be declared as private. The compiler allows non private constructors, but this seems misleading to the reader, since new can never be used with enum types.
  9. Since these enumeration instances are all effectively singletons, they can be compared for equality using identity ("==").
  10. you can use enum in switch statement like int or char primitive data type

In this article, we explored the Java enum from the language basics to more advanced and interesting real-world use cases.

Happy Learning !!

References:

SO Thread
Enum Java Doc
Java 1.5 enumeration