Java Enum Types

The article considers the implementation of Java enum types, starting from the API and ending with examples of the usage of inheritance in enums. The article is recommended reading for all java-programmers, regardless of experience.

Introduction

In programming we frequently encounter a need to limit the set of allowed values for some data type. For example, there are only 7 different values a day of week may take, 12 - for a month in a year, 4 for seasons in a year. To solve similar problems many programming languages with static typing have a special data type - enum. Even though enum was not included into Java from the very beginning, it was introduced since version 1.5. Till then programmers were using other ways to restrict possible variables’ values.

Enum construction

Let’s go ahead and start with an example - let’s implement a data type for seasons storage using Enum:
enum Season {WINTER, SPRING, SUMMER, FALL}
It may be used in a following way:
Season.season = Season.SPRING;

if (season == Season.SPRING) season = Season.SUMMER;

System.out.print(season);
SUMMER will be printed to console as a result of this code’s execution.

Enum is a class

Declaring enum we implicitly create a class derived from java.lang.Enum. Simply speaking, enum Season {...} construction is equivalent to class Season extends java.lang.Enum {...} one. And even if compiler does not allow us to explicitly inherit from java.lang.Enum, the fact that enum inherits from it may be easily checked with the help of reflection:
System.out.println(Season.class.getSuperclass());
Following will be printed to console:
class java.lang.Enum
Therefore, Java compiler automatically implements the inheritance for us. Let’s agree to call the class, that was created by a compiler in order to implement enum - a enum class, and let’s use enum elements to denote set of possible values it may take.

Enum elements are enum class instances that are statically available

Season enum elements (WINTER, SPRING etc) - are statically available instances of class Season enum. Their static availability allows us to compare them using the link
comparator ==. For example,
Season season = Season.SUMMER; 
if (season == Season.AUTUMN) season = Season.WINTER; 

Name and index of enum element

As it was already mentioned earlier, enum class inherits from java.lang.Enum, which in turn contains number of methods that are useful for every enumeration. For example,
Season season = Season.WINTER; 
System.out.println("season.name()=" + season.name() + " season.toString()=" + season.toString() + " season.ordinal()=" + season.ordinal());
The following will be printed out:
season.name()=WINTER season.toString()=WINTER season.ordinal()=0
Above mentioned example illustrates the name(), toString(), and ordinal() methods’ usage. Enum-class inherits those methods from java.lang.Enum class.

Getting access to enum element through its string representation

Quite frequently we would want to get enum element through its string representation. For this purpose in each enum class compiler automatically creates special static method: public static EnumClass valueOf(String name), which returns an element of enum class whose name is equal to name parameter. Example:
String name = "WINTER"; 
Season season = Season.valueOf(name);
season variable will be equal to Season.WINTER as a result of specified code’s execution. It is worth mentioning that IllegalArgumentException will be thrown in case if element can not be found in enum and NullPointerException will be thrown if element is equal to null.

Getting all enum elements

Sometimes we would want to get a list of all enum class elements in a run-time. For this purpose in each enum class compiler creates the following method:
public static EnumClass[] values()
Its usage example:
System.out.println(Arrays.toString(Season.values()));
Its output:
[WINTER, SPRING, SUMMER, AUTUMN]
Pay attention that neither valueOf() nor values() method is defined in java.lang.Enum class. Instead of that they are automatically added by a compiler at enum’s class compilation.

Adding custom methods to enum class

We may add our own custom methods to either enum class or its elements.
enum Direction { 
   UP, DOWN; 
 
   public Direction opposite() { return this == UP ? DOWN : UP; } 
} 
Same but with polymorphism:
enum Direction { 
   UP { 
        public Direction opposite() { return DOWN; } 
   }, 
   DOWN { 
        public Direction opposite() { return UP; } 
   }; 
 
   public abstract Direction opposite(); 
} 
Last example demonstrates inheritance usage in enum.

Inheritance in enum

Class hierarchy whose objects are instantiated only once and are available statically may be implemented in Java with the help of enum. Herewith Enum elements may contain personal constructors.
enum Type { 
    INT(true) { 
        public Object parse(String string) { return Integer.valueOf(string); } 
    }, 
    INTEGER(false) { 
        public Object parse(String string) { return Integer.valueOf(string); } 
    }, 
    STRING(false) { 
        public Object parse(String string) { return string; } 
    }; 
 
    boolean primitive; 
    Type(boolean primitive) { this.primitive = primitive; } 
 
    public boolean isPrimitive() { return primitive; } 
    public abstract Object parse(String string); 
} 
Type enum with three elements (INT, INTEGER and STRING) is declared here. The following classes and objects will be created by a compiler: Type - class derived from java.lang.Enum INT - object of the 1st class derived from Type INTEGER - object of the 2nd class derived from Type STRING - object of the 3rd class derived from Type Three derived classes will be created with a polymorphic method Object parse(String) and Type(..., boolean) constructor. Objects of INT, INTEGER, and STRING classes exist in a single copy and are available statically. It can be checked with the following code:
System.out.println(Type.class); 
System.out.println(Type.INT.getClass() + " " + Type.INT.getClass().getSuperclass()); 
System.out.println(Type.INTEGER.getClass() + " " + Type.INTEGER.getClass().getSuperclass()); 
System.out.println(Type.STRING.getClass()  + " " + Type.STRING.getClass().getSuperclass());
Here is its output:
class Type
class Type$1 class Type
class Type$2 class Type
class Type$3 class Type
As we can see, compiler has created a Type class and 3 nested classes derived from Type.

Decompiled enum class with inheritance

To support our previous claim let’s provide a result of a Type enum decompilation from the previous example:
abstract class Type extends Enum { 
    public static Type[] values() { 
        return (Type[]) $VALUES.clone(); 
    } 
 
    public static Type valueOf(String name) { 
        return (Type) Enum.valueOf(t / T$Type, name); 
    } 
 
    public boolean isPrimitive() { 
        return primitive; 
    } 
 
    public abstract Object parse(String s); 
 
    public static final Type INT; 
    public static final Type INTEGER; 
    public static final Type STRING; 
    boolean primitive; 
    private static final Type $VALUES[]; 
 
    static { 
        INT = new Type("INT", 0, true) { 
            public Object parse(String string) { return Integer.valueOf(string); } 
        }; 
        INTEGER = new Type("INTEGER", 1, false) { 
            public Object parse(String string) { return Integer.valueOf(string); } 
        }; 
        STRING = new Type("STRING", 2, false) { 
            public Object parse(String string) { return string; } 
        }; 
 
        $VALUES = (new Type[]{ 
                INT, INTEGER, STRING 
        }); 
    } 
 
    private Type(String s, int i, boolean primitive) { 
        super(s, i); 
        this.primitive = primitive; 
    } 
} 

Enum and parametric polymorphism

Some may wonder “Why above mentioned Type enum does not use generics?”. It appears that generics’ usage is prohibited in Java. For example, the following code will not compile:
enum Type {}

Further learning

To get a deeper understanding of how Enum works in Java we would recommend to get familiar with java.lang.Enum class source code. Furthermore, Java libraries source code examination is necessary for understanding the working principles of some Java mechanisms.

Original text by Dmitry Pekar

Follow CodeGalaxy

Mobile Beta

Get it on Google Play
Send Feedback
Keep exploring
Java papers
Cosmo
Sign Up Now
or Subscribe for future quizzes